From 9a765e2fcc09e0477bc51ee40db9578040553efd Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 25 Nov 2025 16:17:22 +0100 Subject: [PATCH 001/112] Add verifier with phi resolution --- .../lir/alloc/verifier/RAVInstruction.java | 199 +++++++ .../verifier/RegisterAllocationVerifier.java | 516 ++++++++++++++++++ .../RegisterAllocationVerifierPhase.java | 190 +++++++ .../compiler/lir/phases/AllocationStage.java | 9 + 4 files changed, 914 insertions(+) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java new file mode 100644 index 000000000000..cb93d8bafd60 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -0,0 +1,199 @@ +package jdk.graal.compiler.lir.alloc.verifier; + + +import jdk.graal.compiler.lir.InstructionValueProcedure; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; + +public class RAVInstruction { + public static class Base { + protected LIRInstruction lirInstruction; + protected List virtualMoveList; + + public Base(LIRInstruction lirInstruction) { + this.lirInstruction = lirInstruction; + this.virtualMoveList = new LinkedList<>(); + } + + public LIRInstruction getLIRInstruction() { + return lirInstruction; + } + + public void addVirtualMove(VirtualMove virtualMove) { + this.virtualMoveList.add(virtualMove); + } + + public List getVirtualMoveList() { + return virtualMoveList; + } + } + + private static class GetCountProcedure implements InstructionValueProcedure { + protected int count = 0; + + public int getCount() { + int count = this.count; + // Reset the count and go again for different argument type + this.count = 0; + return count; + } + + @Override + public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + count++; + return value; + } + } + + public static class ValueArrayPair { + protected Value[] orig; + protected Value[] curr; + protected int count; + + public ValueArrayPair(int count) { + this.count = count; + this.curr = new Value[count]; + this.orig = new Value[count]; + } + + public InstructionValueProcedure copyOriginalProc = new InstructionValueProcedure() { + private int index = 0; + + @Override + public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + ValueArrayPair.this.addOrig(index, value); + index++; + return value; + } + }; + + public InstructionValueProcedure copyCurrentProc = new InstructionValueProcedure() { + private int index = 0; + + @Override + public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + ValueArrayPair.this.addCurrent(index, value); + index++; + return value; + } + }; + + public Value getCurrent(int index) { + assert index < this.count; + return this.curr[index]; + } + + public Value getOrig(int index) { + assert index < this.count; + return this.orig[index]; + } + + public void addCurrent(int index, Value value) { + assert index < this.count; + this.curr[index] = value; + } + + public void addOrig(int index, Value value) { + assert index < this.orig.length; + this.orig[index] = value; + } + + /** + * Verify that all presumed values are present and that both sides have it. + * + * @return true, if contents have been successfully verified, false if there's null in either array. + */ + public boolean verifyContents() { + for (int i = 0; i < this.curr.length; i++) { + if (this.curr[i] == null || this.orig[i] == null) { + return false; + } + } + return true; + } + } + + public static class Op extends Base { + public ValueArrayPair dests; + public ValueArrayPair uses; + public ValueArrayPair temp; + public ValueArrayPair alive; + + public Op(LIRInstruction instruction) { + super(instruction); + + var countValuesProc = new GetCountProcedure(); + + instruction.forEachInput(countValuesProc); + this.uses = new ValueArrayPair(countValuesProc.getCount()); + + instruction.forEachOutput(countValuesProc); + this.dests = new ValueArrayPair(countValuesProc.getCount()); + + instruction.forEachTemp(countValuesProc); + this.temp = new ValueArrayPair(countValuesProc.getCount()); + + instruction.forEachAlive(countValuesProc); + this.alive = new ValueArrayPair(countValuesProc.getCount()); + } + + public boolean verifyContents() { + return this.uses.verifyContents() && this.dests.verifyContents() && this.temp.verifyContents() && this.alive.verifyContents(); + } + + public boolean hasMissingDefinitions() { + return this.dests.count > 0 && this.dests.orig[0] instanceof Variable && this.dests.curr[0] == null; + } + } + + public static class Move extends Base { + public RegisterValue from; + public RegisterValue to; + + public Move(LIRInstruction instr, RegisterValue from, RegisterValue to) { + super(instr); + this.from = from; + this.to = to; + } + } + + public static class Reload extends Base { + public VirtualStackSlot from; + public RegisterValue to; + + public Reload(LIRInstruction instr, RegisterValue to, VirtualStackSlot from) { + super(instr); + this.from = from; + this.to = to; + } + } + + public static class Spill extends Base { + public VirtualStackSlot to; + public RegisterValue from; + + public Spill(LIRInstruction instr, VirtualStackSlot to, RegisterValue from) { + super(instr); + this.to = to; + this.from = from; + } + } + + public static class VirtualMove extends Base { + public Variable to; + public RegisterValue from; + + public VirtualMove(LIRInstruction instr, Variable to, RegisterValue from) { + super(instr); + this.to = to; + this.from = from; + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java new file mode 100644 index 000000000000..791c53c63791 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -0,0 +1,516 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +public final class RegisterAllocationVerifier { + public BlockMap> blockInstructions; + public BlockMap blockStates; // Current states + public BlockMap blockEntryStates; // State on entry to block + public LIR lir; + + public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions) { + this.lir = lir; + + var cfg = lir.getControlFlowGraph(); + this.blockInstructions = blockInstructions; + this.blockEntryStates = new BlockMap<>(cfg); + this.blockEntryStates.put(this.lir.getControlFlowGraph().getStartBlock(), new VerifierState()); + + this.blockStates = new BlockMap<>(cfg); + } + + private boolean doPrecessorsHaveStates(BasicBlock block) { + for (int i = 0; i < block.getPredecessorCount(); i++) { + var pred = block.getPredecessorAt(i); + if (this.blockStates.get(pred) == null) { + return false; + } + } + return true; + } + + /** + * Fill in missing variable locations for current block's label instruction and predecessor + * jump instructions. + *

+ * We are looking for intersection between locations of individual processors, this should + * give us a single register that is used for the phi, and is necessary for jump to verify + * that contents are alive that that point and for label to define where the result of phi + * will be held to used in said block. + * + * @param block Block that needs phi function output + * @param labelInstr Label instruction of said block + */ + private void fillMissingVariableLocationsForPhi(BasicBlock block, RAVInstruction.Op labelInstr) { + for (int i = 0; i < labelInstr.dests.count; i++) { + Set locations = null; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + var inputVariable = (Variable) jump.alive.orig[i]; + + var varLoc = state.registerValues.getVariableLocations(inputVariable); + if (locations == null) { + locations = varLoc; + continue; + } + + locations.retainAll(varLoc); + } + + if (locations.size() != 1) { + throw new GraalError("Multiple locations for block " + block); + } + + Value location = locations.stream().findFirst().get(); + labelInstr.dests.curr[i] = location; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + jump.alive.curr[i] = location; + } + } + } + + /** + * For every block, we need to calculate its entry state + * which is a combination of states of blocks that are its + * predecessors, merged into a state we use to verify + * that inputs to instructions are correct symbols based + * on instructions before allocation. + */ + public void calculateEntryBlocks() { + Queue> worklist = new LinkedList<>(); + worklist.add(this.lir.getControlFlowGraph().getStartBlock()); + + while (!worklist.isEmpty()) { + var block = worklist.poll(); + var instructions = this.blockInstructions.get(block); + + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + if (labelInstr.hasMissingDefinitions() && this.doPrecessorsHaveStates(block)) { + this.fillMissingVariableLocationsForPhi(block, labelInstr); + } + + // Create new entry state for successor blocks out of current block state + var state = new VerifierState(this.blockEntryStates.get(block)); + for (var instr : instructions) { + state.update(instr); + } + this.blockStates.put(block, state); + + for (int i = 0; i < block.getSuccessorCount(); i++) { + var succ = block.getSuccessorAt(i); + var succState = this.blockEntryStates.get(succ) == null ? new VerifierState() : this.blockEntryStates.get(succ); + + if (succState.meetWith(state)) { // State changed, add to worklist + this.blockEntryStates.put(succ, succState); + worklist.add(succ); + } + } + } + } + + /** + * Here, use block entries to check if + * inputs to instructions are correct. + * + * @return true, if valid, otherwise false + */ + public boolean verifyInstructionInputs() { + for (int i = 0; i < this.lir.getBlocks().length; i++) { + var block = this.lir.getBlocks()[i]; + var state = this.blockEntryStates.get(block); + var instructions = this.blockInstructions.get(block); + + for (var instr : instructions) { + if (!state.check(instr)) { + return false; + } + state.update(instr); + } + } + + return true; + } + + public boolean run() { + this.calculateEntryBlocks(); + return this.verifyInstructionInputs(); + } + + public class VerifierState { + protected AllocationStateMap registerValues; + protected AllocationStateMap spillSlotValues; + + public VerifierState() { + registerValues = new HashAllocationStateMap<>(); + spillSlotValues = new HashAllocationStateMap<>(); + } + + protected VerifierState(VerifierState other) { + if (other == null) { + registerValues = new HashAllocationStateMap<>(); + spillSlotValues = new HashAllocationStateMap<>(); + return; + } + + registerValues = new HashAllocationStateMap<>(other.getRegisterValues()); + spillSlotValues = new HashAllocationStateMap<>(other.getSpillSlotValues()); + } + + public AllocationStateMap getRegisterValues() { + return registerValues; + } + + public AllocationStateMap getSpillSlotValues() { + return spillSlotValues; + } + + public boolean meetWith(VerifierState other) { + var regChanged = this.registerValues.mergeWith(other.getRegisterValues()); + var stackChanged = this.spillSlotValues.mergeWith(other.getSpillSlotValues()); + return regChanged || stackChanged; + } + + protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { + // Check that incoming values are not unknown or conflicted - these only matter if used + for (int idx = 0; idx < values.count; idx++) { + var orig = values.orig[idx]; + var curr = values.curr[idx]; + + if (orig.equals(curr)) { + // In this case nothing has changed so we have nothing to verify + continue; + } + + var state = this.registerValues.get(curr); + if (state.isConflicted() || state.isUnknown()) { + return false; + } + + if (state instanceof ValueAllocationState valAllocState) { + if (!valAllocState.value.equals(orig)) { + return false; + } + + continue; + } + + throw new IllegalStateException(); // Should never reach here. + } + + return true; + } + + protected boolean checkAliveConstraint(RAVInstruction.Op instruction) { + for (int i = 0; i < instruction.alive.count; i++) { + for (int j = 0; j < instruction.temp.count; j++) { // Cannot be a temp + if (instruction.alive.curr[i].equals(instruction.temp.curr[j])) { + return false; + } + } + + for (int j = 0; j < instruction.uses.count; j++) { // Cannot be a use + if (instruction.alive.curr[i].equals(instruction.uses.curr[j])) { + return false; + } + } + + // I don't think it can be a destination either + for (int j = 0; j < instruction.dests.count; j++) { + if (instruction.alive.curr[i].equals(instruction.dests.curr[j])) { + return false; + } + } + } + + return true; + } + + public boolean check(RAVInstruction.Base instruction) { + if (instruction instanceof RAVInstruction.Op op) { + if (!checkInputs(op.uses)) { + return false; + } + + // if (!checkInputs(op.alive)) { + // // TODO: need to differentiate between alive and use + // // Also, alive cannot be in temp or use, need to enforce this constraint. + // return false; + // } + + // if (!checkInputs(op.temp)) { + // // Memory or registers get passed here and they aren't overwritten by the allocator + // // so checking here comes down to see if arguments are equal. + // // TODO: mark values in registers and stack slots here as dead, so they cannot be used after + // // allocation + // return false; + // } + } + + return true; + } + + public void update(RAVInstruction.Base instruction) { + switch (instruction) { + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + if (Value.ILLEGAL.equals(op.dests.orig[i])) { + continue; // Safe to ignore, when destination is illegal value, not when used. + } + + if (op.dests.curr[i] == null) { + continue; + } + + assert op.dests.orig[i] != null; + + Value variable = op.dests.orig[i]; + this.registerValues.put(op.dests.curr[i], new ValueAllocationState(variable)); + } + } + case RAVInstruction.Spill spill -> + this.spillSlotValues.putClone(spill.to, this.registerValues.get(spill.from)); + case RAVInstruction.Reload reload -> + this.registerValues.putClone(reload.to, this.spillSlotValues.get(reload.from)); + case RAVInstruction.Move move -> + this.registerValues.putClone(move.to, this.registerValues.get(move.from)); + case RAVInstruction.VirtualMove virtMove -> + this.registerValues.put(virtMove.from, new ValueAllocationState(virtMove.to)); + default -> throw new IllegalStateException(); + } + } + } + + public abstract static class AllocationState { + public static AllocationState getDefault() { + return UnknownAllocationState.INSTANCE; + } + + public boolean isUnknown() { + return false; + } + + public boolean isConflicted() { + return false; + } + + public abstract AllocationState clone(); + + public abstract AllocationState meet(AllocationState other); + + public abstract boolean equals(AllocationState other); + } + + public static class ConflictedAllocationState extends AllocationState { + protected List conflictedValues; + + public ConflictedAllocationState(Value value1, Value value2) { + this.conflictedValues = new LinkedList<>(); + this.conflictedValues.add(value1); + this.conflictedValues.add(value2); + } + + private ConflictedAllocationState(List conflictedValues) { + this.conflictedValues = new LinkedList<>(conflictedValues); + } + + public void addConflictedValue(Value value) { + this.conflictedValues.add(value); + } + + public List getConflictedValues() { + return this.conflictedValues; + } + + @Override + public boolean isConflicted() { + return true; + } + + @Override + public AllocationState meet(AllocationState other) { + if (other instanceof ValueAllocationState valueState) { + this.addConflictedValue(valueState.getValue()); + } + + if (other instanceof ConflictedAllocationState conflictedState) { + this.conflictedValues.addAll(conflictedState.conflictedValues); + } + + return this; + } + + @Override + public AllocationState clone() { + return new ConflictedAllocationState(this.conflictedValues); + } + + @Override + public boolean equals(AllocationState other) { + return other.isConflicted(); // TODO: handle contents + } + } + + public static class UnknownAllocationState extends AllocationState { + public static UnknownAllocationState INSTANCE = new UnknownAllocationState(); + + @Override + public boolean isUnknown() { + return true; + } + + @Override + public AllocationState meet(AllocationState other) { + return other; + } + + @Override + public AllocationState clone() { + return INSTANCE; + } + + @Override + public boolean equals(AllocationState other) { + return other == INSTANCE; + } + } + + public static class ValueAllocationState extends AllocationState implements Cloneable { + protected Value value; + + public ValueAllocationState(Value value) { + if (value instanceof RegisterValue || value instanceof Variable) { + // We use variables as symbols for register validation + // but real registers can also be used as that, in some cases. + this.value = value; + } else { + throw new IllegalStateException(); + } + } + + public ValueAllocationState(ValueAllocationState other) { + this.value = other.getValue(); + } + + public Value getValue() { + return value; + } + + public AllocationState meet(AllocationState other) { + if (other.isUnknown()) { + return this; + } + + if (other.isConflicted()) { + return other; + } + + var otherValueAllocState = (ValueAllocationState) other; + if (!this.value.equals(otherValueAllocState.getValue())) { + return new ConflictedAllocationState(this.value, otherValueAllocState.getValue()); + } + + return this; + } + + @Override + public boolean equals(AllocationState other) { + return other instanceof ValueAllocationState otherVal && this.value.equals(otherVal.getValue()); + } + + @Override + public ValueAllocationState clone() { + return new ValueAllocationState(this); + } + } + + public interface AllocationStateMap extends Map, Cloneable { + AllocationState get(Object key); + + AllocationState putClone(K key, AllocationState value); + + boolean mergeWith(AllocationStateMap other); + + Set getVariableLocations(Variable variable); + } + + @SuppressWarnings("serial") + public class HashAllocationStateMap extends HashMap implements AllocationStateMap { + public HashAllocationStateMap() { + super(); + } + + public HashAllocationStateMap(AllocationStateMap other) { + super(other); + } + + @Override + public AllocationState get(Object key) { + var value = super.get(key); + if (value == null) { + return AllocationState.getDefault(); + } + + return value; + } + + public Set getVariableLocations(Variable variable) { + Set locations = new HashSet<>(); + for (Map.Entry entry : entrySet()) { + if (entry.getValue() instanceof ValueAllocationState valState) { + if (valState.getValue().equals(variable)) { + locations.add(entry.getKey()); + } + } + } + return locations; + } + + public AllocationState putClone(K key, AllocationState value) { + if (value.isConflicted() || value.isUnknown()) { + return this.put(key, value); + } + + return this.put(key, value.clone()); + } + + @Override + public boolean mergeWith(AllocationStateMap source) { + boolean changed = false; + for (var entry : source.entrySet()) { + if (!this.containsKey(entry.getKey())) { + changed = true; + + this.put(entry.getKey(), UnknownAllocationState.INSTANCE); + } + + var currentValue = this.get(entry.getKey()); + var result = this.get(entry.getKey()).meet(entry.getValue()); + if (!currentValue.equals(result)) { + changed = true; + } + + this.put(entry.getKey(), result); + } + + return changed; + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java new file mode 100644 index 000000000000..404cd0320e03 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -0,0 +1,190 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.graal.compiler.lir.gen.LIRGenerationResult; +import jdk.graal.compiler.lir.phases.AllocationPhase; +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionKey; +import jdk.graal.compiler.options.OptionType; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.TargetDescription; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class RegisterAllocationVerifierPhase extends AllocationPhase { + public static class Options { + @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) + public static final OptionKey EnableRAVerifier = new OptionKey<>(false); + } + + protected RAVerifierPreAllocPhase preallocPhaseRAVerifier; + + public AllocationPhase getPreAllocPhase() { + this.preallocPhaseRAVerifier = new RAVerifierPreAllocPhase(); + return this.preallocPhaseRAVerifier; + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + assert this.preallocPhaseRAVerifier != null : "Phase before register allocation was not run, cannot verify it."; + + var instructions = this.getVerifierInstructions(lirGenRes.getLIR()); + var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions); + if (!verifier.run()) { + // TODO: every error generated by the verifier needs to be a lot more verbose. + // throw new IllegalStateException("Could not verify the register allocation..."); + // Exception handler + + System.err.println("Verification failed - " + lirGenRes.getCompilationUnitName()); + } + } + + /** + * Create Register Verifier Instruction that was created by the Register Allocator. + * Generally speaking, it's always a move instruction, other ones return null. + * + * @param instruction LIRInstruction newly created by Register Allocator + * @return Spill, Reload, Move or null if instruction is not a move + */ + protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return null; + } + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + + var input = valueMov.getInput(); + var result = valueMov.getResult(); + + if (input instanceof VirtualStackSlot stackSlot && result instanceof RegisterValue reg) { + return new RAVInstruction.Reload(instruction, reg, stackSlot); + } else if (result instanceof VirtualStackSlot stackSlot && input instanceof RegisterValue reg) { + return new RAVInstruction.Spill(instruction, stackSlot, reg); + } else if (input instanceof RegisterValue reg1 && result instanceof RegisterValue reg2) { + return new RAVInstruction.Move(instruction, reg1, reg2); + } + + return null; + } + + protected BlockMap> getVerifierInstructions(LIR lir) { + BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); + var preallocMap = preallocPhaseRAVerifier.getPreallocMap(); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + var instructionList = new LinkedList(); + + ArrayList instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + var rAVInstr = preallocMap.get(instruction); + if (rAVInstr == null) { + var movOp = this.getRAVMoveInstruction(instruction); + if (movOp != null) { + instructionList.add(movOp); + continue; + } + + // TODO: handle constants in Phi's + + throw new GraalError("Unknown instruction type for verification - " + instruction); + } + + var opRAVInstr = (RAVInstruction.Op) rAVInstr; + + instruction.forEachInput(opRAVInstr.uses.copyCurrentProc); + instruction.forEachOutput(opRAVInstr.dests.copyCurrentProc); + instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); + instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); + + // assert opRAVInstr.verifyContents(); + + instructionList.add(opRAVInstr); + var virtualMoves = opRAVInstr.getVirtualMoveList(); + instructionList.addAll(virtualMoves); + } + + blockInstructions.put(block, instructionList); + } + return blockInstructions; + } + + public static class RAVerifierPreAllocPhase extends AllocationPhase { + protected Map preallocMap; + + protected RAVerifierPreAllocPhase() { + this.preallocMap = new HashMap<>(); + } + + public Map getPreallocMap() { + return preallocMap; + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + LIR lir = lirGenRes.getLIR(); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + ArrayList instructions = lir.getLIRforBlock(block); + + RAVInstruction.Base previousInstr = null; + for (var instruction : instructions) { + if (this.isVirtualMove(instruction)) { + // Virtual moves (variable = MOV real register) are going to be removed by the allocator, + // but we still need the information about which variables are associated to which real + // registers, and so we store them. They are generally associated to other instructions + // that's why we append them here to the previous instruction (for example Label or Foreign Call) + // use these, if this instruction was deleted in the allocator, then they will be missing too. + assert previousInstr != null; + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var register = (RegisterValue) valueMov.getInput(); + var variable = (Variable) valueMov.getResult(); + + var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); + previousInstr.addVirtualMove(virtualMove); + continue; // No need to store virtual move here, it is stored into previous instruction. + } + + var opRAVInstr = new RAVInstruction.Op(instruction); + + instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); + instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); + instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); + instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); + + this.preallocMap.put(instruction, opRAVInstr); + + previousInstr = opRAVInstr; + } + } + } + + /** + * Determines if instruction is a virtual move, a virtual move is + * a move instruction that moves a real register value into a variable, + * which is something that will always get removed from the final allocated + * IR. + * + * @param instruction LIRInstruction we are looking at + * @return true, if instruction is a virtual move, otherwise false + */ + protected boolean isVirtualMove(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return false; + } + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + return valueMov.getInput() instanceof RegisterValue && valueMov.getResult() instanceof Variable; + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java index 801ba4519cf1..dcd4660d1b1e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java @@ -26,6 +26,7 @@ import jdk.graal.compiler.debug.Assertions; import jdk.graal.compiler.lir.alloc.AllocationStageVerifier; +import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhase; import jdk.graal.compiler.lir.stackslotalloc.LSStackSlotAllocator; import jdk.graal.compiler.lir.stackslotalloc.SimpleStackSlotAllocator; import jdk.graal.compiler.lir.alloc.lsra.LinearScanPhase; @@ -39,6 +40,14 @@ public AllocationStage(OptionValues options) { appendPhase(new MarkBasePointersPhase()); appendPhase(new LinearScanPhase()); + // For now, verify before stack allocator + if (RegisterAllocationVerifierPhase.Options.EnableRAVerifier.getValue(options)) { + var raPhase = new RegisterAllocationVerifierPhase(); + + prependPhase(raPhase.getPreAllocPhase()); + appendPhase(raPhase); + } + // build frame map if (LSStackSlotAllocator.Options.LIROptLSStackSlotAllocator.getValue(options)) { appendPhase(new LSStackSlotAllocator()); From 09ac5b833bbaf422cb2ac390fc6a7eec0889970b Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 25 Nov 2025 19:50:35 +0100 Subject: [PATCH 002/112] Handle constants and simple loops --- .../lir/alloc/verifier/RAVInstruction.java | 4 +-- .../verifier/RegisterAllocationVerifier.java | 34 ++++++++++++++----- .../RegisterAllocationVerifierPhase.java | 12 +++++++ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index cb93d8bafd60..81214397e819 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -187,10 +187,10 @@ public Spill(LIRInstruction instr, VirtualStackSlot to, RegisterValue from) { } public static class VirtualMove extends Base { - public Variable to; + public Value to; public RegisterValue from; - public VirtualMove(LIRInstruction instr, Variable to, RegisterValue from) { + public VirtualMove(LIRInstruction instr, Value to, RegisterValue from) { super(instr); this.to = to; this.from = from; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 791c53c63791..10e61b24f973 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -3,6 +3,7 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.Variable; import jdk.vm.ci.code.RegisterValue; @@ -55,16 +56,16 @@ private boolean doPrecessorsHaveStates(BasicBlock block) { * @param block Block that needs phi function output * @param labelInstr Label instruction of said block */ - private void fillMissingVariableLocationsForPhi(BasicBlock block, RAVInstruction.Op labelInstr) { + private void resolveVariableLocationsForPhi(BasicBlock block, RAVInstruction.Op labelInstr) { for (int i = 0; i < labelInstr.dests.count; i++) { Set locations = null; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); var state = this.blockStates.get(pred); var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - var inputVariable = (Variable) jump.alive.orig[i]; + var inputValue = jump.alive.orig[i]; - var varLoc = state.registerValues.getVariableLocations(inputVariable); + var varLoc = state.registerValues.getValueLocations(inputValue); if (locations == null) { locations = varLoc; continue; @@ -103,8 +104,10 @@ public void calculateEntryBlocks() { var instructions = this.blockInstructions.get(block); var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + var changedLabelArgs = false; if (labelInstr.hasMissingDefinitions() && this.doPrecessorsHaveStates(block)) { - this.fillMissingVariableLocationsForPhi(block, labelInstr); + this.resolveVariableLocationsForPhi(block, labelInstr); + changedLabelArgs = true; } // Create new entry state for successor blocks out of current block state @@ -116,7 +119,20 @@ public void calculateEntryBlocks() { for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); - var succState = this.blockEntryStates.get(succ) == null ? new VerifierState() : this.blockEntryStates.get(succ); + + VerifierState succState; + if (this.blockEntryStates.get(succ) == null || changedLabelArgs) { + succState = new VerifierState(); + + // Either there's no state because it was not yet processed first part of the condition + // or, we need to reset it because label changed, second part of the condition + + // Label change will hopefully work for children of children and there won't be need for us + // to reset all the children. + this.blockStates.put(succ, null); + } else { + succState = this.blockEntryStates.get(succ); + } if (succState.meetWith(state)) { // State changed, add to worklist this.blockEntryStates.put(succ, succState); @@ -396,7 +412,7 @@ public static class ValueAllocationState extends AllocationState implements Clon protected Value value; public ValueAllocationState(Value value) { - if (value instanceof RegisterValue || value instanceof Variable) { + if (value instanceof RegisterValue || value instanceof Variable || value instanceof ConstantValue) { // We use variables as symbols for register validation // but real registers can also be used as that, in some cases. this.value = value; @@ -448,7 +464,7 @@ public interface AllocationStateMap extends Map, Cloneabl boolean mergeWith(AllocationStateMap other); - Set getVariableLocations(Variable variable); + Set getValueLocations(Value value); } @SuppressWarnings("serial") @@ -471,11 +487,11 @@ public AllocationState get(Object key) { return value; } - public Set getVariableLocations(Variable variable) { + public Set getValueLocations(Value value) { Set locations = new HashSet<>(); for (Map.Entry entry : entrySet()) { if (entry.getValue() instanceof ValueAllocationState valState) { - if (valState.getValue().equals(variable)) { + if (valState.getValue().equals(value)) { locations.add(entry.getKey()); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 404cd0320e03..a29b2a2c370c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -1,8 +1,10 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.StandardOp; @@ -59,6 +61,16 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo */ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { if (!instruction.isValueMoveOp()) { + if (instruction.isLoadConstantOp()) { + var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); + var constant = constatLoad.getConstant(); + var result = (RegisterValue) constatLoad.getResult(); + + // This isn't really a virtual move, but it currently acts the same, so we keep it, + // we take constants as variables. TODO: maybe remove virtual move altogether for Move(reg, var/constant) + return new RAVInstruction.VirtualMove(instruction, new ConstantValue(result.getValueKind(), constant), result); + } + return null; } var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); From d3dc97de28d45832ab06282aacde9b33b9090c5e Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 25 Nov 2025 21:04:24 +0100 Subject: [PATCH 003/112] Fix StackSlot as label argument not working --- .../lir/alloc/verifier/RAVInstruction.java | 18 +++++++-------- .../verifier/RegisterAllocationVerifier.java | 23 +++++++++++++++---- .../RegisterAllocationVerifierPhase.java | 14 +++++++---- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 81214397e819..19f4fc5655e3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -165,10 +165,10 @@ public Move(LIRInstruction instr, RegisterValue from, RegisterValue to) { } public static class Reload extends Base { - public VirtualStackSlot from; + public Value from; public RegisterValue to; - public Reload(LIRInstruction instr, RegisterValue to, VirtualStackSlot from) { + public Reload(LIRInstruction instr, RegisterValue to, Value from) { super(instr); this.from = from; this.to = to; @@ -176,10 +176,10 @@ public Reload(LIRInstruction instr, RegisterValue to, VirtualStackSlot from) { } public static class Spill extends Base { - public VirtualStackSlot to; + public Value to; public RegisterValue from; - public Spill(LIRInstruction instr, VirtualStackSlot to, RegisterValue from) { + public Spill(LIRInstruction instr, Value to, RegisterValue from) { super(instr); this.to = to; this.from = from; @@ -187,13 +187,13 @@ public Spill(LIRInstruction instr, VirtualStackSlot to, RegisterValue from) { } public static class VirtualMove extends Base { - public Value to; - public RegisterValue from; + public Value variableOrConstant; + public Value location; - public VirtualMove(LIRInstruction instr, Value to, RegisterValue from) { + public VirtualMove(LIRInstruction instr, Value variableOrConstant, Value location) { super(instr); - this.to = to; - this.from = from; + this.variableOrConstant = variableOrConstant; + this.location = location; } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 10e61b24f973..b82874d5d1b0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -7,6 +7,7 @@ import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.Variable; import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.Value; import java.util.HashMap; @@ -53,7 +54,7 @@ private boolean doPrecessorsHaveStates(BasicBlock block) { * that contents are alive that that point and for label to define where the result of phi * will be held to used in said block. * - * @param block Block that needs phi function output + * @param block Block that needs phi function output * @param labelInstr Label instruction of said block */ private void resolveVariableLocationsForPhi(BasicBlock block, RAVInstruction.Op labelInstr) { @@ -215,7 +216,13 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { continue; } - var state = this.registerValues.get(curr); + AllocationState state; + if (curr instanceof RegisterValue) { + state = this.registerValues.get(curr); + } else { + state = this.spillSlotValues.get(curr); + } + if (state.isConflicted() || state.isUnknown()) { return false; } @@ -307,8 +314,13 @@ public void update(RAVInstruction.Base instruction) { this.registerValues.putClone(reload.to, this.spillSlotValues.get(reload.from)); case RAVInstruction.Move move -> this.registerValues.putClone(move.to, this.registerValues.get(move.from)); - case RAVInstruction.VirtualMove virtMove -> - this.registerValues.put(virtMove.from, new ValueAllocationState(virtMove.to)); + case RAVInstruction.VirtualMove virtMove -> { + if (virtMove.location instanceof RegisterValue) { + this.registerValues.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); + } else { + this.spillSlotValues.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); + } + } default -> throw new IllegalStateException(); } } @@ -412,9 +424,10 @@ public static class ValueAllocationState extends AllocationState implements Clon protected Value value; public ValueAllocationState(Value value) { - if (value instanceof RegisterValue || value instanceof Variable || value instanceof ConstantValue) { + if (value instanceof RegisterValue || value instanceof Variable || value instanceof ConstantValue || value instanceof StackSlot) { // We use variables as symbols for register validation // but real registers can also be used as that, in some cases. + // TODO: reconsider handling of StackSlots this.value = value; } else { throw new IllegalStateException(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index a29b2a2c370c..207a466a4a4a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -16,6 +16,7 @@ import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionType; import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; import java.util.ArrayList; @@ -84,6 +85,10 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) return new RAVInstruction.Spill(instruction, stackSlot, reg); } else if (input instanceof RegisterValue reg1 && result instanceof RegisterValue reg2) { return new RAVInstruction.Move(instruction, reg1, reg2); + } else if (input instanceof StackSlot stackSlot && result instanceof RegisterValue reg) { + return new RAVInstruction.Reload(instruction, reg, stackSlot); + } else if (input instanceof RegisterValue reg && result instanceof StackSlot stackSlot) { + return new RAVInstruction.Spill(instruction, stackSlot, reg); } return null; @@ -106,8 +111,6 @@ protected BlockMap> getVerifierInstructions(LIR lir) { continue; } - // TODO: handle constants in Phi's - throw new GraalError("Unknown instruction type for verification - " + instruction); } @@ -159,10 +162,10 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo assert previousInstr != null; var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var register = (RegisterValue) valueMov.getInput(); + var location = valueMov.getInput(); var variable = (Variable) valueMov.getResult(); - var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); + var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, location); previousInstr.addVirtualMove(virtualMove); continue; // No need to store virtual move here, it is stored into previous instruction. } @@ -196,7 +199,8 @@ protected boolean isVirtualMove(LIRInstruction instruction) { } var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - return valueMov.getInput() instanceof RegisterValue && valueMov.getResult() instanceof Variable; + var input = valueMov.getInput(); + return (input instanceof RegisterValue || input instanceof StackSlot) && valueMov.getResult() instanceof Variable; } } } From 51efb03f99ca05be099a1b7f318b8a219f433ec4 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 25 Nov 2025 21:23:54 +0100 Subject: [PATCH 004/112] Make sure both previous and current values are present --- .../jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java | 4 ---- .../lir/alloc/verifier/RegisterAllocationVerifier.java | 4 ++++ .../lir/alloc/verifier/RegisterAllocationVerifierPhase.java | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 19f4fc5655e3..41931f2e5100 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -144,10 +144,6 @@ public Op(LIRInstruction instruction) { this.alive = new ValueArrayPair(countValuesProc.getCount()); } - public boolean verifyContents() { - return this.uses.verifyContents() && this.dests.verifyContents() && this.temp.verifyContents() && this.alive.verifyContents(); - } - public boolean hasMissingDefinitions() { return this.dests.count > 0 && this.dests.orig[0] instanceof Variable && this.dests.curr[0] == null; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index b82874d5d1b0..02b78ceb6039 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -211,6 +211,8 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { var orig = values.orig[idx]; var curr = values.curr[idx]; + assert curr != null; + if (orig.equals(curr)) { // In this case nothing has changed so we have nothing to verify continue; @@ -299,6 +301,8 @@ public void update(RAVInstruction.Base instruction) { } if (op.dests.curr[i] == null) { + // This can happen for certain instructions - jump or label, and we need to + // resolve appropriate registers for these, if we do not, we throw in check() continue; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 207a466a4a4a..c5d6580120df 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -121,8 +121,6 @@ protected BlockMap> getVerifierInstructions(LIR lir) { instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); - // assert opRAVInstr.verifyContents(); - instructionList.add(opRAVInstr); var virtualMoves = opRAVInstr.getVirtualMoveList(); instructionList.addAll(virtualMoves); From 869275336ae58966bafcedadf87d41dff2ef43b8 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 2 Dec 2025 19:31:41 +0100 Subject: [PATCH 005/112] Resolve phi variables from usage --- .../lir/alloc/verifier/RAVInstruction.java | 11 ++- .../verifier/RegisterAllocationVerifier.java | 91 +++++++++++++++++++ .../RegisterAllocationVerifierPhase.java | 44 ++++++++- 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 41931f2e5100..1ecdc4a16c92 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -4,7 +4,6 @@ import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.Variable; -import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; @@ -16,10 +15,12 @@ public class RAVInstruction { public static class Base { protected LIRInstruction lirInstruction; protected List virtualMoveList; + protected List speculativeMoveList; public Base(LIRInstruction lirInstruction) { this.lirInstruction = lirInstruction; this.virtualMoveList = new LinkedList<>(); + this.speculativeMoveList = new LinkedList<>(); } public LIRInstruction getLIRInstruction() { @@ -33,6 +34,14 @@ public void addVirtualMove(VirtualMove virtualMove) { public List getVirtualMoveList() { return virtualMoveList; } + + public void addSpeculativeMove(VirtualMove virtualMove) { + this.speculativeMoveList.add(virtualMove); + } + + public List getSpeculativeMoveList() { + return speculativeMoveList; + } } private static class GetCountProcedure implements InstructionValueProcedure { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 02b78ceb6039..a427e2dd298a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -22,6 +22,7 @@ public final class RegisterAllocationVerifier { public BlockMap> blockInstructions; public BlockMap blockStates; // Current states public BlockMap blockEntryStates; // State on entry to block + public Map labelMap; public LIR lir; public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions) { @@ -33,6 +34,7 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.blockEntryStates.put(this.lir.getControlFlowGraph().getStartBlock(), new VerifierState()); this.blockStates = new BlockMap<>(cfg); + this.labelMap = new HashMap<>(); } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -45,6 +47,94 @@ private boolean doPrecessorsHaveStates(BasicBlock block) { return true; } + private void mapLabelVariableFromUsage( + // @formatter:off + Map> labelToBlockMap, + Variable variable, RegisterValue register) + // @formatter:on + { + var variableLabelInstr = this.labelMap.get(variable); + if (variableLabelInstr == null) { + return; + } + + for (int j = 0; j < variableLabelInstr.dests.count; j++) { + if (variableLabelInstr.dests.orig[j].equals(variable)) { + variableLabelInstr.dests.curr[j] = register; + this.labelMap.remove(variable); + + // Need to iterate over predecessors and fill jumps as well + var labelBlock = labelToBlockMap.get(variableLabelInstr); + for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { + var pred = labelBlock.getPredecessorAt(k); + var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); + jumpOp.alive.curr[j] = variable; + } + } + } + } + + /** + * Resolves label variable registers by finding where they are used. + */ + private void resolveLabelsFromUsage() { + Queue> worklist = new LinkedList<>(); + worklist.add(this.lir.getControlFlowGraph().getStartBlock()); + + Map> labelToBlockMap = new HashMap<>(); + + Set visited = new HashSet<>(); + while (!worklist.isEmpty()) { + var block = worklist.poll(); + visited.add(block.getId()); + + var instructions = this.blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + for (int i = 0; i < labelInstr.dests.count; i++) { + if (labelInstr.dests.curr[i] == null) { + Variable variable = (Variable) labelInstr.dests.orig[i]; + this.labelMap.put(variable, labelInstr); + labelToBlockMap.put(labelInstr, block); + } + } + + for (var instruction : instructions) { + switch (instruction) { + case RAVInstruction.VirtualMove move -> { + if (move.variableOrConstant instanceof Variable variable && move.location instanceof RegisterValue register) { + this.mapLabelVariableFromUsage(labelToBlockMap, variable, register); + } + } + case RAVInstruction.Op op -> { + for (int i = 0; i < op.uses.count; i++) { + if (op.uses.orig[i] instanceof Variable variable && op.uses.curr[i] instanceof RegisterValue register) { + this.mapLabelVariableFromUsage(labelToBlockMap, variable, register); + } + } + } + default -> { + } + } + } + + for (int i = 0; i < block.getSuccessorCount(); i++) { + var succ = block.getSuccessorAt(i); + if (visited.contains(succ.getId())) { + continue; + } + // TODO: save path from label to usage and determine if value was spilled, reloaded, moved, etc... + worklist.add(succ); + } + } + + if (!this.labelMap.isEmpty()) { + // Maybe throwing is not the best way to do things, if variable is unused, we do not care + // which register is used, but if in other case we haven't determined the register + // And it is used somewhere, we need to mark the error with message that we could not determine it. + throw new GraalError("Was not able to determine all label registers."); + } + } + /** * Fill in missing variable locations for current block's label instruction and predecessor * jump instructions. @@ -167,6 +257,7 @@ public boolean verifyInstructionInputs() { } public boolean run() { + this.resolveLabelsFromUsage(); this.calculateEntryBlocks(); return this.verifyInstructionInputs(); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index c5d6580120df..e17e48030744 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -1,6 +1,5 @@ package jdk.graal.compiler.lir.alloc.verifier; -import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.GraalError; @@ -21,9 +20,11 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; public class RegisterAllocationVerifierPhase extends AllocationPhase { public static class Options { @@ -95,6 +96,13 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) } protected BlockMap> getVerifierInstructions(LIR lir) { + Set presentInstructions = new HashSet<>(); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + ArrayList instructions = lir.getLIRforBlock(block); + presentInstructions.addAll(instructions); + } + BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); var preallocMap = preallocPhaseRAVerifier.getPreallocMap(); for (var blockId : lir.getBlocks()) { @@ -122,6 +130,16 @@ protected BlockMap> getVerifierInstructions(LIR lir) { instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); instructionList.add(opRAVInstr); + var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); + + if (!speculativeMoves.isEmpty()) { + for (var speculativeMove : speculativeMoves) { + if (!presentInstructions.contains(speculativeMove.getLIRInstruction())) { + instructionList.add(speculativeMove); + } + } + } + var virtualMoves = opRAVInstr.getVirtualMoveList(); instructionList.addAll(virtualMoves); } @@ -168,6 +186,21 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo continue; // No need to store virtual move here, it is stored into previous instruction. } + if (this.isSpeculativeMove(instruction)) { + // Speculative moves are in form ry = MOVE vx, which could be removed if variable + // ends up being allocated to the same register as ry. If it was removed + // we need to re-add it because it holds important information about where value of + // this variable is placed - for label resolution after the label. + assert previousInstr != null; + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var variable = (Variable) valueMov.getInput(); + var register = (RegisterValue) valueMov.getResult(); + + var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); + previousInstr.addSpeculativeMove(virtualMove); + } + var opRAVInstr = new RAVInstruction.Op(instruction); instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); @@ -200,5 +233,14 @@ protected boolean isVirtualMove(LIRInstruction instruction) { var input = valueMov.getInput(); return (input instanceof RegisterValue || input instanceof StackSlot) && valueMov.getResult() instanceof Variable; } + + protected boolean isSpeculativeMove(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return false; + } + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + return valueMov.getResult() instanceof RegisterValue && valueMov.getInput() instanceof Variable; + } } } From ffe73e69856be4275ed2d406a8a1d4de9fb402db Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 2 Dec 2025 22:10:17 +0100 Subject: [PATCH 006/112] Optionally, resolve from jumps --- .../lir/alloc/verifier/PhiResolution.java | 6 ++ .../verifier/RegisterAllocationVerifier.java | 71 ++++++++++--- .../RegisterAllocationVerifierPhase.java | 100 +++++++++++++++++- .../compiler/lir/phases/AllocationStage.java | 2 +- 4 files changed, 160 insertions(+), 19 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java new file mode 100644 index 000000000000..fe583df7744a --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java @@ -0,0 +1,6 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +public enum PhiResolution { + FromJump, + FromUsage +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index a427e2dd298a..5aa11b866caa 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -24,8 +24,9 @@ public final class RegisterAllocationVerifier { public BlockMap blockEntryStates; // State on entry to block public Map labelMap; public LIR lir; + public PhiResolution phiResolution; - public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions) { + public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution) { this.lir = lir; var cfg = lir.getControlFlowGraph(); @@ -35,6 +36,8 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.blockStates = new BlockMap<>(cfg); this.labelMap = new HashMap<>(); + + this.phiResolution = phiResolution; } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -179,6 +182,45 @@ private void resolveVariableLocationsForPhi(BasicBlock block, RAVInstruction. } } + /** + * Resolve phi registers from this jump point, can only be done if + * the phi variables/constants only have one location. + * + *

+ * This way we can handle phi arguments before we reach said block. + * But cannot really be done for loops, if their initial values + * are the same and there are multiple ones. + *

+ * + * @param block From whom we are jumping from + */ + private void resolvePhiFromJump(BasicBlock block) { + var state = this.blockStates.get(block); + var jumpInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getLast(); + for (int i = 0; i < jumpInstr.alive.count; i++) { + if (jumpInstr.alive.curr[i] != null) { + continue; + } + + var inputValue = jumpInstr.alive.orig[i]; + if (inputValue instanceof Variable || inputValue instanceof ConstantValue) { + // Does not work for constants if there is more of them + var locations = state.registerValues.getValueLocations(inputValue); + if (locations.size() == 1) { + var register = locations.stream().findFirst().get(); + jumpInstr.alive.curr[i] = register; + } + + for (int j = 0; j < block.getSuccessorCount(); j++) { + var succ = block.getSuccessorAt(j); + var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); + + labelInstr.dests.curr[i] = jumpInstr.alive.curr[i]; + } + } + } + } + /** * For every block, we need to calculate its entry state * which is a combination of states of blocks that are its @@ -194,13 +236,6 @@ public void calculateEntryBlocks() { var block = worklist.poll(); var instructions = this.blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - var changedLabelArgs = false; - if (labelInstr.hasMissingDefinitions() && this.doPrecessorsHaveStates(block)) { - this.resolveVariableLocationsForPhi(block, labelInstr); - changedLabelArgs = true; - } - // Create new entry state for successor blocks out of current block state var state = new VerifierState(this.blockEntryStates.get(block)); for (var instr : instructions) { @@ -208,11 +243,15 @@ public void calculateEntryBlocks() { } this.blockStates.put(block, state); + if (this.phiResolution == PhiResolution.FromJump) { + this.resolvePhiFromJump(block); + } + for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); VerifierState succState; - if (this.blockEntryStates.get(succ) == null || changedLabelArgs) { + if (this.blockEntryStates.get(succ) == null) { succState = new VerifierState(); // Either there's no state because it was not yet processed first part of the condition @@ -257,7 +296,9 @@ public boolean verifyInstructionInputs() { } public boolean run() { - this.resolveLabelsFromUsage(); + if (this.phiResolution == PhiResolution.FromUsage) { + this.resolveLabelsFromUsage(); + } this.calculateEntryBlocks(); return this.verifyInstructionInputs(); } @@ -442,23 +483,23 @@ public boolean isConflicted() { } public static class ConflictedAllocationState extends AllocationState { - protected List conflictedValues; + protected Set conflictedValues; public ConflictedAllocationState(Value value1, Value value2) { - this.conflictedValues = new LinkedList<>(); + this.conflictedValues = new HashSet<>(); this.conflictedValues.add(value1); this.conflictedValues.add(value2); } - private ConflictedAllocationState(List conflictedValues) { - this.conflictedValues = new LinkedList<>(conflictedValues); + private ConflictedAllocationState(Set conflictedValues) { + this.conflictedValues = new HashSet<>(conflictedValues); } public void addConflictedValue(Value value) { this.conflictedValues.add(value); } - public List getConflictedValues() { + public Set getConflictedValues() { return this.conflictedValues; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index e17e48030744..e89925a6928e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -7,18 +7,26 @@ import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.ValueProcedure; import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; +import jdk.graal.compiler.lir.gen.LIRGenerator; import jdk.graal.compiler.lir.phases.AllocationPhase; +import jdk.graal.compiler.options.EnumOptionKey; 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.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.Value; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -30,12 +38,21 @@ public class RegisterAllocationVerifierPhase extends AllocationPhase { public static class Options { @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) public static final OptionKey EnableRAVerifier = new OptionKey<>(false); + + @Option(help = "", type = OptionType.Debug) + public static final EnumOptionKey RAPhiResolution = new EnumOptionKey<>(PhiResolution.FromUsage); + } + + private PhiResolution phiResolution; + + public RegisterAllocationVerifierPhase(OptionValues options) { + this.phiResolution = Options.RAPhiResolution.getValue(options); } protected RAVerifierPreAllocPhase preallocPhaseRAVerifier; public AllocationPhase getPreAllocPhase() { - this.preallocPhaseRAVerifier = new RAVerifierPreAllocPhase(); + this.preallocPhaseRAVerifier = new RAVerifierPreAllocPhase(phiResolution); return this.preallocPhaseRAVerifier; } @@ -43,8 +60,9 @@ public AllocationPhase getPreAllocPhase() { protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { assert this.preallocPhaseRAVerifier != null : "Phase before register allocation was not run, cannot verify it."; + var instructions = this.getVerifierInstructions(lirGenRes.getLIR()); - var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions); + var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.phiResolution); if (!verifier.run()) { // TODO: every error generated by the verifier needs to be a lot more verbose. // throw new IllegalStateException("Could not verify the register allocation..."); @@ -149,11 +167,56 @@ protected BlockMap> getVerifierInstructions(LIR lir) { return blockInstructions; } + private static class ConstantOverrideValueProcedure implements ValueProcedure { + private LIR lir; + private List variables; + private Map constantValueMap; + private Method nextVariableMethod; + + public ConstantOverrideValueProcedure(LIR lir, Map constantValueMap) { + this.lir = lir; + this.constantValueMap = constantValueMap; + + try { + this.nextVariableMethod = LIRGenerator.VariableProvider.class.getDeclaredMethod("nextVariable"); + this.nextVariableMethod.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public void setVariableList(List variables) { + this.variables = variables; + } + + public int nextVariable() { + try { + return (int) nextVariableMethod.invoke((LIRGenerator.VariableProvider) this.lir); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (value instanceof ConstantValue constant) { + var variable = new Variable(constant.getValueKind(), nextVariable()); + constantValueMap.put(variable, constant); + variables.add(variable); + return variable; + } + + return value; + } + } + public static class RAVerifierPreAllocPhase extends AllocationPhase { protected Map preallocMap; + protected PhiResolution phiResolution; - protected RAVerifierPreAllocPhase() { + protected RAVerifierPreAllocPhase(PhiResolution phiResolution) { this.preallocMap = new HashMap<>(); + this.phiResolution = phiResolution; } public Map getPreallocMap() { @@ -162,13 +225,29 @@ public Map getPreallocMap() { @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + Map constantValueMap = new HashMap<>(); + LIR lir = lirGenRes.getLIR(); + var constOverwriteProc = new ConstantOverrideValueProcedure(lir, constantValueMap); + for (var blockId : lir.getBlocks()) { BasicBlock block = lir.getBlockById(blockId); ArrayList instructions = lir.getLIRforBlock(block); + List newVars = new LinkedList<>(); + constOverwriteProc.setVariableList(newVars); + RAVInstruction.Base previousInstr = null; for (var instruction : instructions) { + if ( + // @formatter:off + instruction instanceof StandardOp.JumpOp + && this.phiResolution == PhiResolution.FromJump + // @formatter:on + ) { + instruction.forEachAlive(constOverwriteProc); + } + if (this.isVirtualMove(instruction)) { // Virtual moves (variable = MOV real register) are going to be removed by the allocator, // but we still need the information about which variables are associated to which real @@ -212,6 +291,21 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo previousInstr = opRAVInstr; } + + if (newVars.isEmpty()) { + continue; + } + + var j = instructions.removeLast(); + for (var v : newVars) { + var mov = context.spillMoveFactory.createMove(v, constantValueMap.get(v)); + var ravInstr = new RAVInstruction.Op(mov); + mov.forEachOutput(ravInstr.dests.copyOriginalProc); + this.preallocMap.put(mov, ravInstr); + instructions.add(mov); + } + + instructions.add(j); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java index dcd4660d1b1e..0ce08ca32c48 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java @@ -42,7 +42,7 @@ public AllocationStage(OptionValues options) { // For now, verify before stack allocator if (RegisterAllocationVerifierPhase.Options.EnableRAVerifier.getValue(options)) { - var raPhase = new RegisterAllocationVerifierPhase(); + var raPhase = new RegisterAllocationVerifierPhase(options); prependPhase(raPhase.getPreAllocPhase()); appendPhase(raPhase); From 3a7a664104679e88ed0a3a0b7eaf1075dca34ba0 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 3 Dec 2025 20:21:57 +0100 Subject: [PATCH 007/112] Check for kind constraints --- .../verifier/RegisterAllocationVerifier.java | 64 ++++++++++++------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 5aa11b866caa..2fbacfef79fe 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -71,7 +71,7 @@ private void mapLabelVariableFromUsage( for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { var pred = labelBlock.getPredecessorAt(k); var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); - jumpOp.alive.curr[j] = variable; + jumpOp.alive.curr[j] = register; } } } @@ -357,12 +357,17 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { state = this.spillSlotValues.get(curr); } + if (!orig.getValueKind().equals(curr.getValueKind())) { + return false; // Kind do not match + } + if (state.isConflicted() || state.isUnknown()) { return false; } if (state instanceof ValueAllocationState valAllocState) { if (!valAllocState.value.equals(orig)) { + // Kind sizes should be checked here as well. return false; } @@ -377,19 +382,12 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { protected boolean checkAliveConstraint(RAVInstruction.Op instruction) { for (int i = 0; i < instruction.alive.count; i++) { - for (int j = 0; j < instruction.temp.count; j++) { // Cannot be a temp + for (int j = 0; j < instruction.temp.count; j++) { if (instruction.alive.curr[i].equals(instruction.temp.curr[j])) { return false; } } - for (int j = 0; j < instruction.uses.count; j++) { // Cannot be a use - if (instruction.alive.curr[i].equals(instruction.uses.curr[j])) { - return false; - } - } - - // I don't think it can be a destination either for (int j = 0; j < instruction.dests.count; j++) { if (instruction.alive.curr[i].equals(instruction.dests.curr[j])) { return false; @@ -406,19 +404,23 @@ public boolean check(RAVInstruction.Base instruction) { return false; } - // if (!checkInputs(op.alive)) { - // // TODO: need to differentiate between alive and use - // // Also, alive cannot be in temp or use, need to enforce this constraint. - // return false; - // } - - // if (!checkInputs(op.temp)) { - // // Memory or registers get passed here and they aren't overwritten by the allocator - // // so checking here comes down to see if arguments are equal. - // // TODO: mark values in registers and stack slots here as dead, so they cannot be used after - // // allocation - // return false; - // } + if (!checkInputs(op.alive)) { + return false; + } + + for (int i = 0; i < op.temp.count; i++) { + var curr = op.temp.curr[i]; + var orig = op.temp.orig[i]; + + if (!curr.getValueKind().equals(orig.getValueKind())) { + // Make sure the assigned register has the correct kind for temp. + return false; + } + } + + if (!this.checkAliveConstraint(op)) { + return false; + } } return true; @@ -443,6 +445,11 @@ public void update(RAVInstruction.Base instruction) { Value variable = op.dests.orig[i]; this.registerValues.put(op.dests.curr[i], new ValueAllocationState(variable)); } + + for (int i = 0; i < op.temp.count; i++) { + // We cannot believe the contents of registers used as temp, thus we need to reset. + this.registerValues.put(op.temp.curr[i], UnknownAllocationState.INSTANCE); + } } case RAVInstruction.Spill spill -> this.spillSlotValues.putClone(spill.to, this.registerValues.get(spill.from)); @@ -626,6 +633,11 @@ public HashAllocationStateMap(AllocationStateMap other) { super(other); } + @Override + public AllocationState put(K key, AllocationState value) { + return super.put(key, value); + } + @Override public AllocationState get(Object key) { var value = super.get(key); @@ -677,5 +689,13 @@ public boolean mergeWith(AllocationStateMap source) { return changed; } + + // TODO: we need to not use size of the work for the hash map for registers at least + private String getCorrectKeyString(Value value) { + return switch (value) { + case RegisterValue reg -> reg.getRegister().toString(); + default -> value.toString(); + }; + } } } From d80f0b81ef6b589d74ef008ca7e7fd86955bc553 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 9 Dec 2025 18:29:53 +0100 Subject: [PATCH 008/112] Get target register by walking from first usage backwards --- .../verifier/RegisterAllocationVerifier.java | 104 +++++++++++++++--- 1 file changed, 90 insertions(+), 14 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 2fbacfef79fe..16cdc465ccf7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -18,6 +18,7 @@ import java.util.Queue; import java.util.Set; +// TODO: handle GC instructions public final class RegisterAllocationVerifier { public BlockMap> blockInstructions; public BlockMap blockStates; // Current states @@ -50,10 +51,78 @@ private boolean doPrecessorsHaveStates(BasicBlock block) { return true; } + private RegisterValue getRegisterFromUsage( + LinkedList> path, + BasicBlock defBlock, + RAVInstruction.Base usageInstruction, + RegisterValue register, + Variable variable + ) { + while (!path.isEmpty()) { + var block = path.peek(); + if (block == defBlock) { + break; + } + + path.poll(); + } + + Value stackSlot = null; + boolean reachedUsage = false; + for (var block : path.reversed()) { // TODO: reconsider circular paths + var instructions = this.blockInstructions.get(block).reversed(); + for (var instruction : instructions) { + if (instruction == usageInstruction) { + reachedUsage = true; + continue; + } + + if (!reachedUsage) { + continue; + } + + // Tracking the value bottom up, from the usage up to the label definition + // looking for any changes to the target register that could highlight + // different register is supposed to be used, in case of reload/spill combo or + // register move. If we are wrong about the target register then, it will + // get thrown out in the verification stage. TODO: maybe try to use multiple usages to be sure? + switch (instruction) { + case RAVInstruction.Spill spill -> { + if (spill.to.equals(stackSlot)) { + register = spill.from; + } + } + case RAVInstruction.Move move -> { + if (move.to.equals(register)) { + register = move.from; + } + } + case RAVInstruction.Reload reload -> { + if (reload.to.equals(register)) { + register = null; // No longer holds the variable + stackSlot = reload.from; + } + } + case RAVInstruction.VirtualMove move -> { + if (move.location.equals(register) && !move.variableOrConstant.equals(variable)) { + throw new Error("Target register has different variable."); // TODO: deal with this when we find an example + } + } + // For Op, if there is a redefinition, we let the later stages handle that + default -> { + } + } + } + } + + return register; + } + private void mapLabelVariableFromUsage( // @formatter:off Map> labelToBlockMap, - Variable variable, RegisterValue register) + Variable variable, RegisterValue regGuess, + LinkedList> path, RAVInstruction.Base useInstruction) // @formatter:on { var variableLabelInstr = this.labelMap.get(variable); @@ -61,13 +130,14 @@ private void mapLabelVariableFromUsage( return; } + var labelBlock = labelToBlockMap.get(variableLabelInstr); + var register = this.getRegisterFromUsage(path, labelBlock, useInstruction, regGuess, variable); for (int j = 0; j < variableLabelInstr.dests.count; j++) { if (variableLabelInstr.dests.orig[j].equals(variable)) { variableLabelInstr.dests.curr[j] = register; this.labelMap.remove(variable); // Need to iterate over predecessors and fill jumps as well - var labelBlock = labelToBlockMap.get(variableLabelInstr); for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { var pred = labelBlock.getPredecessorAt(k); var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); @@ -81,15 +151,19 @@ private void mapLabelVariableFromUsage( * Resolves label variable registers by finding where they are used. */ private void resolveLabelsFromUsage() { - Queue> worklist = new LinkedList<>(); - worklist.add(this.lir.getControlFlowGraph().getStartBlock()); + Queue>> worklist = new LinkedList<>(); + + LinkedList> firstPath = new LinkedList<>(); // TODO: maybe use something better than LinkedList? + firstPath.add(this.lir.getControlFlowGraph().getStartBlock()); + worklist.add(firstPath); Map> labelToBlockMap = new HashMap<>(); - Set visited = new HashSet<>(); + Set> visited = new HashSet<>(); while (!worklist.isEmpty()) { - var block = worklist.poll(); - visited.add(block.getId()); + var path = worklist.poll(); + var block = path.getLast(); + visited.add(block); var instructions = this.blockInstructions.get(block); var labelInstr = (RAVInstruction.Op) instructions.getFirst(); @@ -105,13 +179,13 @@ private void resolveLabelsFromUsage() { switch (instruction) { case RAVInstruction.VirtualMove move -> { if (move.variableOrConstant instanceof Variable variable && move.location instanceof RegisterValue register) { - this.mapLabelVariableFromUsage(labelToBlockMap, variable, register); + this.mapLabelVariableFromUsage(labelToBlockMap, variable, register, path, instruction); } } case RAVInstruction.Op op -> { for (int i = 0; i < op.uses.count; i++) { if (op.uses.orig[i] instanceof Variable variable && op.uses.curr[i] instanceof RegisterValue register) { - this.mapLabelVariableFromUsage(labelToBlockMap, variable, register); + this.mapLabelVariableFromUsage(labelToBlockMap, variable, register, path, instruction); } } } @@ -122,11 +196,13 @@ private void resolveLabelsFromUsage() { for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); - if (visited.contains(succ.getId())) { + if (visited.contains(succ)) { continue; } - // TODO: save path from label to usage and determine if value was spilled, reloaded, moved, etc... - worklist.add(succ); + + var nextPath = new LinkedList<>(path); + nextPath.add(succ); + worklist.add(nextPath); } } @@ -358,7 +434,7 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { } if (!orig.getValueKind().equals(curr.getValueKind())) { - return false; // Kind do not match + return false; // Kind do not match } if (state.isConflicted() || state.isUnknown()) { @@ -405,7 +481,7 @@ public boolean check(RAVInstruction.Base instruction) { } if (!checkInputs(op.alive)) { - return false; + return false; } for (int i = 0; i < op.temp.count; i++) { From cefa34268221c1240b5407ff5afdbd420b9cd920 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 10 Dec 2025 00:02:03 +0100 Subject: [PATCH 009/112] Resolve variables from predecessor states --- .../lir/alloc/verifier/PhiResolution.java | 3 +- .../verifier/RegisterAllocationVerifier.java | 167 +++++++++++++++++- 2 files changed, 161 insertions(+), 9 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java index fe583df7744a..1a0ff18b47ac 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java @@ -2,5 +2,6 @@ public enum PhiResolution { FromJump, - FromUsage + FromUsage, + FromPredecessors } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 16cdc465ccf7..be1981ba523c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -27,6 +27,8 @@ public final class RegisterAllocationVerifier { public LIR lir; public PhiResolution phiResolution; + public BlockMap blockDefinitions; + public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution) { this.lir = lir; @@ -39,6 +41,8 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.labelMap = new HashMap<>(); this.phiResolution = phiResolution; + + this.blockDefinitions = new BlockMap<>(cfg); } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -150,7 +154,7 @@ private void mapLabelVariableFromUsage( /** * Resolves label variable registers by finding where they are used. */ - private void resolveLabelsFromUsage() { + private void resolvePhiFromUsage() { Queue>> worklist = new LinkedList<>(); LinkedList> firstPath = new LinkedList<>(); // TODO: maybe use something better than LinkedList? @@ -226,7 +230,7 @@ private void resolveLabelsFromUsage() { * @param block Block that needs phi function output * @param labelInstr Label instruction of said block */ - private void resolveVariableLocationsForPhi(BasicBlock block, RAVInstruction.Op labelInstr) { + private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { for (int i = 0; i < labelInstr.dests.count; i++) { Set locations = null; for (int j = 0; j < block.getPredecessorCount(); j++) { @@ -241,11 +245,13 @@ private void resolveVariableLocationsForPhi(BasicBlock block, RAVInstruction. continue; } + // TODO: maybe we just need the locations to have size 1 to stop locations.retainAll(varLoc); } if (locations.size() != 1) { - throw new GraalError("Multiple locations for block " + block); + // throw new GraalError("Multiple locations for block " + block); + return false; } Value location = locations.stream().findFirst().get(); @@ -256,6 +262,93 @@ private void resolveVariableLocationsForPhi(BasicBlock block, RAVInstruction. jump.alive.curr[i] = location; } } + + return true; + } + + private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { + var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); + + // Definition block needs to have this set. + var propagateMap = new HashMap, List>(); + var variableToRegisters = new HashMap(); + var defBlockVariablesToPropagate = new LinkedList(); + var defForEntry = this.blockDefinitions.get(defBlock); + for (int i = 0; i < labelInstr.dests.count; i++) { + var register = (RegisterValue) labelInstr.dests.curr[i]; + var variable = (Variable) labelInstr.dests.orig[i]; + + var registerDefinition = defForEntry.registerValues.get(register); + if (registerDefinition.isUnknown()) { + defForEntry.registerValues.put(register, new ValueAllocationState(variable)); + } + + defBlockVariablesToPropagate.add(variable); + variableToRegisters.put(variable, register); + } + + Queue> worklist = new LinkedList<>(); + Set> processed = new HashSet<>(); + worklist.add(defBlock); + propagateMap.put(defBlock, defBlockVariablesToPropagate); + + while (!worklist.isEmpty()) { + var curr = worklist.remove(); + if (processed.contains(curr)) { + continue; + } + processed.add(curr); + + var def = this.blockDefinitions.get(curr); + + var variablesToPropagate = propagateMap.get(curr); + var variablesToBePropagated = new LinkedList(); + for (var variable : variablesToPropagate) { + var register = variableToRegisters.get(variable); + + var registerDefinition = def.registerValues.get(register); + if (!registerDefinition.isUnknown() && registerDefinition instanceof ValueAllocationState valState && !valState.getValue().equals(variable)) { + // This block has redefined the value of said register, + // and we will not pass it further. + continue; + } + + variablesToBePropagated.add(variable); + } + + if (variablesToBePropagated.isEmpty()) { + continue; + } + + for (int i = 0; i < curr.getSuccessorCount(); i++) { + var succ = curr.getSuccessorAt(i); + var succEntryState = this.blockEntryStates.get(succ); + if (succEntryState == null) { + continue; + } + + if (succ.equals(defBlock)) { + // This means that the definition block would have same value as predecessor + // for example: B0 defines [v0] in label, B1 is it's successor as well as it's + // predecessor, and if it does not overwrite this register, it would change + // entry state for B0 to include v0, which is defined by B0. + throw new GraalError("Circular definition for variable detected."); + } + + boolean dominates = succ.dominates(defBlock); + if (dominates) { + continue; + } + + for (var variable : variablesToBePropagated) { + var register = variableToRegisters.get(variable); + succEntryState.registerValues.put(register, new ValueAllocationState(variable)); + } + + propagateMap.put(succ, variablesToBePropagated); + worklist.add(succ); + } + } } /** @@ -282,21 +375,42 @@ private void resolvePhiFromJump(BasicBlock block) { if (inputValue instanceof Variable || inputValue instanceof ConstantValue) { // Does not work for constants if there is more of them var locations = state.registerValues.getValueLocations(inputValue); - if (locations.size() == 1) { - var register = locations.stream().findFirst().get(); - jumpInstr.alive.curr[i] = register; + if (locations.size() != 1) { + continue; } + var register = locations.stream().findFirst().get(); + jumpInstr.alive.curr[i] = register; for (int j = 0; j < block.getSuccessorCount(); j++) { var succ = block.getSuccessorAt(j); var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); labelInstr.dests.curr[i] = jumpInstr.alive.curr[i]; + + for (int k = 0; k < succ.getPredecessorCount(); k++) { + // Sibling jumps need to be updated as well in order to pass input checks. + var sibling = succ.getPredecessorAt(k); + if (sibling.equals(block)) { + continue; + } + + var siblingJumpInstr = (RAVInstruction.Op) this.blockInstructions.get(sibling).getLast(); + siblingJumpInstr.alive.curr[i] = jumpInstr.alive.curr[i]; + } } } } } + private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { + for (int i = 0; i < op.dests.count; i++) { + if (op.dests.curr[i] == null) { + return true; + } + } + return false; + } + /** * For every block, we need to calculate its entry state * which is a combination of states of blocks that are its @@ -312,6 +426,19 @@ public void calculateEntryBlocks() { var block = worklist.poll(); var instructions = this.blockInstructions.get(block); + boolean resolvedPhi = false; + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + if (this.phiResolution == PhiResolution.FromPredecessors && this.doPrecessorsHaveStates(block) && this.hasMissingRegistersInLabel(labelInstr)) { + resolvedPhi = this.resolvePhiFromPredecessors(block, labelInstr); + } + + if (this.phiResolution == PhiResolution.FromJump && this.hasMissingRegistersInLabel(labelInstr)) { + if (!doPrecessorsHaveStates(block)) { + // A hot fix, if not all predecessors have state, then we wait until all of them do + continue; + } + } + // Create new entry state for successor blocks out of current block state var state = new VerifierState(this.blockEntryStates.get(block)); for (var instr : instructions) { @@ -323,6 +450,10 @@ public void calculateEntryBlocks() { this.resolvePhiFromJump(block); } + if (resolvedPhi) { + this.propagateLabelChangeFromPredecessors(block); + } + for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); @@ -342,6 +473,7 @@ public void calculateEntryBlocks() { if (succState.meetWith(state)) { // State changed, add to worklist this.blockEntryStates.put(succ, succState); + worklist.remove(succ); // Always at the end, for predecessors to be processed first. worklist.add(succ); } } @@ -373,8 +505,24 @@ public boolean verifyInstructionInputs() { public boolean run() { if (this.phiResolution == PhiResolution.FromUsage) { - this.resolveLabelsFromUsage(); + this.resolvePhiFromUsage(); + } + + if (this.phiResolution == PhiResolution.FromPredecessors) { + // Currently only useful to this resolution type, but later + // we should only go through instructions once and then just merge + // with entry state and successors. + for (var blockId : this.lir.getBlocks()) { + var block = this.lir.getBlockById(blockId); + var instructions = this.blockInstructions.get(block); + var state = new VerifierState(); + for (var instr : instructions) { + state.update(instr); + } + this.blockDefinitions.put(block, state); + } } + this.calculateEntryBlocks(); return this.verifyInstructionInputs(); } @@ -510,12 +658,15 @@ public void update(RAVInstruction.Base instruction) { continue; // Safe to ignore, when destination is illegal value, not when used. } - if (op.dests.curr[i] == null) { + if (phiResolution == PhiResolution.FromPredecessors && op.dests.curr[i] == null) { // This can happen for certain instructions - jump or label, and we need to // resolve appropriate registers for these, if we do not, we throw in check() continue; } + // Here, FromJump resolution will fail if it was not completed + // + assert op.dests.curr[i] != null; assert op.dests.orig[i] != null; Value variable = op.dests.orig[i]; From 25128e794169111894c0961c2e8c1b7378f03612 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 5 Jan 2026 15:19:54 +0100 Subject: [PATCH 010/112] Fix issues stemming from unit tests --- .../lir/alloc/verifier/RAVInstruction.java | 37 +- .../verifier/RegisterAllocationVerifier.java | 419 +++++++++++++++--- .../RegisterAllocationVerifierPhase.java | 74 +++- 3 files changed, 444 insertions(+), 86 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 1ecdc4a16c92..88d17ee04f8f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -3,8 +3,9 @@ import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; -import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.Value; import java.util.EnumSet; @@ -18,6 +19,8 @@ public static class Base { protected List speculativeMoveList; public Base(LIRInstruction lirInstruction) { + // We do not actually need the original instruction here, + // but is useful to have when debugging. this.lirInstruction = lirInstruction; this.virtualMoveList = new LinkedList<>(); this.speculativeMoveList = new LinkedList<>(); @@ -95,22 +98,22 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope }; public Value getCurrent(int index) { - assert index < this.count; + assert index < this.count : "Index out of bounds"; return this.curr[index]; } public Value getOrig(int index) { - assert index < this.count; + assert index < this.count : "Index out of bounds"; return this.orig[index]; } public void addCurrent(int index, Value value) { - assert index < this.count; + assert index < this.count : "Index out of bounds"; this.curr[index] = value; } public void addOrig(int index, Value value) { - assert index < this.orig.length; + assert index < this.orig.length : "Index out of bounds"; this.orig[index] = value; } @@ -154,7 +157,12 @@ public Op(LIRInstruction instruction) { } public boolean hasMissingDefinitions() { - return this.dests.count > 0 && this.dests.orig[0] instanceof Variable && this.dests.curr[0] == null; + for (int i = 0; i < this.dests.count; i++) { + if (this.dests.curr[i] == null) { + return true; + } + } + return false; } } @@ -169,6 +177,23 @@ public Move(LIRInstruction instr, RegisterValue from, RegisterValue to) { } } + // StackMove class to handle STACKMOVE instruction, temporary for now + // before I decide how all Moves should be handled + all possible combinations. + public static class StackMove extends Base { + public Value from; + public Value to; + + public StackMove(LIRInstruction instr, Value from, Value to) { + super(instr); + + assert from instanceof StackSlot || from instanceof VirtualStackSlot : "StackMove needs to receive instanceof StackSlot or VirtualStackSlot"; + assert to instanceof StackSlot || to instanceof VirtualStackSlot : "StackMove needs to receive instanceof StackSlot or VirtualStackSlot"; + + this.from = from; + this.to = to; + } + } + public static class Reload extends Base { public Value from; public RegisterValue to; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index be1981ba523c..170630d8ad98 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -1,15 +1,23 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.LIRKindWithCast; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.CastValue; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -18,15 +26,20 @@ import java.util.Queue; import java.util.Set; -// TODO: handle GC instructions public final class RegisterAllocationVerifier { public BlockMap> blockInstructions; public BlockMap blockStates; // Current states public BlockMap blockEntryStates; // State on entry to block - public Map labelMap; + public LIR lir; public PhiResolution phiResolution; + // FromUsage resolver + public Map labelMap; + public Map variableRegisterMap; + public Map usageAliasMap; + + // FromPredecessors resolver public BlockMap blockDefinitions; public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution) { @@ -38,11 +51,14 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.blockEntryStates.put(this.lir.getControlFlowGraph().getStartBlock(), new VerifierState()); this.blockStates = new BlockMap<>(cfg); - this.labelMap = new HashMap<>(); this.phiResolution = phiResolution; this.blockDefinitions = new BlockMap<>(cfg); + + this.labelMap = new HashMap<>(); + this.variableRegisterMap = new HashMap<>(); + this.usageAliasMap = new HashMap<>(); } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -136,10 +152,14 @@ private void mapLabelVariableFromUsage( var labelBlock = labelToBlockMap.get(variableLabelInstr); var register = this.getRegisterFromUsage(path, labelBlock, useInstruction, regGuess, variable); + + this.variableRegisterMap.put(variable, register); + this.labelMap.remove(variable); + this.usageAliasMap.remove(variable); + for (int j = 0; j < variableLabelInstr.dests.count; j++) { if (variableLabelInstr.dests.orig[j].equals(variable)) { variableLabelInstr.dests.curr[j] = register; - this.labelMap.remove(variable); // Need to iterate over predecessors and fill jumps as well for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { @@ -149,6 +169,35 @@ private void mapLabelVariableFromUsage( } } } + + for (var aliasEntry : this.usageAliasMap.entrySet()) { + var originalVariable = LIRValueUtil.asVariable(aliasEntry.getValue()); + if (!originalVariable.equals(variable)) { + continue; + } + + var aliasVariable = LIRValueUtil.asVariable(aliasEntry.getKey()); + var aliasLabelInstr = this.labelMap.get(aliasVariable); + if (aliasLabelInstr == null) { + continue; + } + + this.labelMap.remove(aliasVariable); + + var aliasLabelBlock = labelToBlockMap.get(aliasLabelInstr); + for (int j = 0; j < aliasLabelInstr.dests.count; j++) { + if (aliasLabelInstr.dests.orig[j].equals(aliasVariable)) { + aliasLabelInstr.dests.curr[j] = register; + + // Need to iterate over predecessors and fill jumps as well + for (int k = 0; k < aliasLabelBlock.getPredecessorCount(); k++) { + var pred = aliasLabelBlock.getPredecessorAt(k); + var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); + jumpOp.alive.curr[j] = register; + } + } + } + } } /** @@ -157,7 +206,13 @@ private void mapLabelVariableFromUsage( private void resolvePhiFromUsage() { Queue>> worklist = new LinkedList<>(); - LinkedList> firstPath = new LinkedList<>(); // TODO: maybe use something better than LinkedList? + // TODO: need to store paths in a more memory friendly way + // because we always copy the whole path when entering successor + // which is unnecessary and explodes in memory usage, making + // this method by far the most resource demanding, while + // not being the best. + + LinkedList> firstPath = new LinkedList<>(); firstPath.add(this.lir.getControlFlowGraph().getStartBlock()); worklist.add(firstPath); @@ -167,13 +222,20 @@ private void resolvePhiFromUsage() { while (!worklist.isEmpty()) { var path = worklist.poll(); var block = path.getLast(); + + if (visited.contains(block)) { + // TODO: for some reason blocks that were already visited + // are present here which causes few issues. + continue; + } + visited.add(block); var instructions = this.blockInstructions.get(block); var labelInstr = (RAVInstruction.Op) instructions.getFirst(); for (int i = 0; i < labelInstr.dests.count; i++) { if (labelInstr.dests.curr[i] == null) { - Variable variable = (Variable) labelInstr.dests.orig[i]; + Variable variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); this.labelMap.put(variable, labelInstr); labelToBlockMap.put(labelInstr, block); } @@ -182,14 +244,37 @@ private void resolvePhiFromUsage() { for (var instruction : instructions) { switch (instruction) { case RAVInstruction.VirtualMove move -> { - if (move.variableOrConstant instanceof Variable variable && move.location instanceof RegisterValue register) { - this.mapLabelVariableFromUsage(labelToBlockMap, variable, register, path, instruction); + if (LIRValueUtil.isVariable(move.variableOrConstant) && move.location instanceof RegisterValue register) { + this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(move.variableOrConstant), register, path, instruction); } } case RAVInstruction.Op op -> { for (int i = 0; i < op.uses.count; i++) { - if (op.uses.orig[i] instanceof Variable variable && op.uses.curr[i] instanceof RegisterValue register) { - this.mapLabelVariableFromUsage(labelToBlockMap, variable, register, path, instruction); + if (LIRValueUtil.isVariable(op.uses.orig[i]) && op.uses.curr[i] instanceof RegisterValue register) { + this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(op.uses.orig[i]), register, path, instruction); + } + } + + if (instruction.lirInstruction instanceof StandardOp.JumpOp) { + // Always only one successor for this jump op + // Assumption here is, that we resolve aliases with original registers immediately + // so in-case an alias was defined after that happened, it's not resolved and will fail. + var label = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); + for (int i = 0; i < op.alive.count; i++) { + if (!LIRValueUtil.isVariable(op.alive.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(op.alive.orig[i]); + if (this.labelMap.get(variable) != null) { + this.usageAliasMap.put(variable, LIRValueUtil.asVariable(label.dests.orig[i])); + } + } + } else { + for (int i = 0; i < op.alive.count; i++) { + if (LIRValueUtil.isVariable(op.alive.orig[i]) && op.alive.curr[i] instanceof RegisterValue register) { + this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(op.alive.orig[i]), register, path, instruction); + } } } } @@ -210,12 +295,9 @@ private void resolvePhiFromUsage() { } } - if (!this.labelMap.isEmpty()) { - // Maybe throwing is not the best way to do things, if variable is unused, we do not care - // which register is used, but if in other case we haven't determined the register - // And it is used somewhere, we need to mark the error with message that we could not determine it. - throw new GraalError("Was not able to determine all label registers."); - } + // We no longer throw an error when label map is not empty + // because if such thing happens, then variable was not used, + // and thus we cannot determine its location. } /** @@ -232,7 +314,7 @@ private void resolvePhiFromUsage() { */ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; + Set locations = null; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); var state = this.blockStates.get(pred); @@ -250,16 +332,17 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O } if (locations.size() != 1) { - // throw new GraalError("Multiple locations for block " + block); return false; } - Value location = locations.stream().findFirst().get(); - labelInstr.dests.curr[i] = location; + Register location = locations.stream().findFirst().get(); + var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); + + labelInstr.dests.curr[i] = registerValue; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = location; + jump.alive.curr[i] = registerValue; } } @@ -271,12 +354,12 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { // Definition block needs to have this set. var propagateMap = new HashMap, List>(); - var variableToRegisters = new HashMap(); + var variableToRegisters = new HashMap(); var defBlockVariablesToPropagate = new LinkedList(); var defForEntry = this.blockDefinitions.get(defBlock); for (int i = 0; i < labelInstr.dests.count; i++) { - var register = (RegisterValue) labelInstr.dests.curr[i]; - var variable = (Variable) labelInstr.dests.orig[i]; + var register = ValueUtil.asRegister(labelInstr.dests.curr[i]); + var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); var registerDefinition = defForEntry.registerValues.get(register); if (registerDefinition.isUnknown()) { @@ -300,10 +383,13 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { processed.add(curr); var def = this.blockDefinitions.get(curr); + var state = this.blockStates.get(curr); var variablesToPropagate = propagateMap.get(curr); + var itToPropagate = variablesToPropagate.iterator(); var variablesToBePropagated = new LinkedList(); - for (var variable : variablesToPropagate) { + while (itToPropagate.hasNext()) { + var variable = LIRValueUtil.asVariable(itToPropagate.next()); var register = variableToRegisters.get(variable); var registerDefinition = def.registerValues.get(register); @@ -314,6 +400,10 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { } variablesToBePropagated.add(variable); + + if (state != null) { + state.registerValues.put(register, new ValueAllocationState(variable)); + } } if (variablesToBePropagated.isEmpty()) { @@ -340,7 +430,9 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { continue; } - for (var variable : variablesToBePropagated) { + var itToBePropagated = variablesToBePropagated.iterator(); + while (itToBePropagated.hasNext()) { + var variable = LIRValueUtil.asVariable(itToBePropagated.next()); var register = variableToRegisters.get(variable); succEntryState.registerValues.put(register, new ValueAllocationState(variable)); } @@ -372,15 +464,28 @@ private void resolvePhiFromJump(BasicBlock block) { } var inputValue = jumpInstr.alive.orig[i]; - if (inputValue instanceof Variable || inputValue instanceof ConstantValue) { + if (LIRValueUtil.isVariable(inputValue) || inputValue instanceof ConstantValue) { // Does not work for constants if there is more of them var locations = state.registerValues.getValueLocations(inputValue); - if (locations.size() != 1) { + if (locations.isEmpty()) { continue; } var register = locations.stream().findFirst().get(); - jumpInstr.alive.curr[i] = register; + if (locations.size() != 1) { + int time = -1; + for (var location : locations) { + var locTime = state.registerValues.getTime(location); + if (locTime > time) { + register = location; + time = locTime; + } + } + } + + var registerValue = register.asValue(jumpInstr.alive.orig[i].getValueKind()); + jumpInstr.alive.curr[i] = registerValue; + for (int j = 0; j < block.getSuccessorCount(); j++) { var succ = block.getSuccessorAt(j); var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); @@ -403,12 +508,7 @@ private void resolvePhiFromJump(BasicBlock block) { } private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { - for (int i = 0; i < op.dests.count; i++) { - if (op.dests.curr[i] == null) { - return true; - } - } - return false; + return op.hasMissingDefinitions(); } /** @@ -471,12 +571,63 @@ public void calculateEntryBlocks() { succState = this.blockEntryStates.get(succ); } - if (succState.meetWith(state)) { // State changed, add to worklist + var succLabelOp = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); + if (succState.meetWith(state) || this.hasMissingRegistersInLabel(succLabelOp)) { + // if we resolved a phi, then we also need to process children again, + // TODO: might be better to do something in propagateLabelChangeFromPredecessors + + // State changed or labels have not yet been determined, add to worklist this.blockEntryStates.put(succ, succState); worklist.remove(succ); // Always at the end, for predecessors to be processed first. worklist.add(succ); } } + + if (phiResolution == PhiResolution.FromPredecessors && worklist.isEmpty()) { + this.addMissingLabelBlocks(worklist); + } + } + } + + private int missingLabelBlocksCount = -1; + private int missingLabelCheckRunCount = 0; + + /** + * This method needs to be run, because sometimes already processed blocks + * do not get their labels updated with newly defined variables and thus + * resolution is not complete. + * + * TODO: this might be better be handled by some dependency graph + * + * @param worklist Worklist to which we add new blocks for processing. + */ + private void addMissingLabelBlocks(Queue> worklist) { + if (missingLabelBlocksCount != -1 && missingLabelBlocksCount >= missingLabelCheckRunCount) { + // First time around it counts number of missing label blocks + // then it uses said count to make sure this function is not ran more + // than said amount of times to prevent infinite cycles, + // this can happen if there's a dependency loop between said label blocks + // and should be avoided with other resolution methods. + return; + } + + missingLabelCheckRunCount++; + int currentMissingLabelBlockCount = 0; + for (var blockId : this.lir.getBlocks()) { + var pBlock = this.lir.getBlockById(blockId); + var label = (RAVInstruction.Op) this.blockInstructions.get(pBlock).getFirst(); + if (this.hasMissingRegistersInLabel(label)) { + if (!this.doPrecessorsHaveStates(pBlock)) { + throw new RuntimeException("Could not determine label registers for " + pBlock); + } + + currentMissingLabelBlockCount++; + worklist.add(pBlock); + } + } + + if (missingLabelBlocksCount == -1) { + missingLabelBlocksCount = currentMissingLabelBlockCount; } } @@ -528,8 +679,8 @@ public boolean run() { } public class VerifierState { - protected AllocationStateMap registerValues; - protected AllocationStateMap spillSlotValues; + protected HashAllocationStateMap registerValues; + protected HashAllocationStateMap spillSlotValues; public VerifierState() { registerValues = new HashAllocationStateMap<>(); @@ -547,11 +698,11 @@ protected VerifierState(VerifierState other) { spillSlotValues = new HashAllocationStateMap<>(other.getSpillSlotValues()); } - public AllocationStateMap getRegisterValues() { + public HashAllocationStateMap getRegisterValues() { return registerValues; } - public AllocationStateMap getSpillSlotValues() { + public HashAllocationStateMap getSpillSlotValues() { return spillSlotValues; } @@ -561,13 +712,26 @@ public boolean meetWith(VerifierState other) { return regChanged || stackChanged; } - protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { + protected boolean checkInputs(RAVInstruction.ValueArrayPair values, boolean isJump) { // Check that incoming values are not unknown or conflicted - these only matter if used for (int idx = 0; idx < values.count; idx++) { var orig = values.orig[idx]; var curr = values.curr[idx]; - assert curr != null; + if (curr == null && isJump && phiResolution == PhiResolution.FromUsage) { + // Whenever PhiResolution = FromUsage, variable is not used and thus no register present. + continue; + } + + assert orig != null; + + if (curr == null) { + if (isJump) { + throw new RuntimeException(this.getMissingLabelOrJumpErrMsg("JUMP", values)); + } + + assert false; + } if (orig.equals(curr)) { // In this case nothing has changed so we have nothing to verify @@ -576,13 +740,13 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { AllocationState state; if (curr instanceof RegisterValue) { - state = this.registerValues.get(curr); + state = this.registerValues.get(ValueUtil.asRegister(curr)); } else { state = this.spillSlotValues.get(curr); } - if (!orig.getValueKind().equals(curr.getValueKind())) { - return false; // Kind do not match + if (!kindsEqual(orig, curr)) { + return false; } if (state.isConflicted() || state.isUnknown()) { @@ -591,6 +755,11 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { if (state instanceof ValueAllocationState valAllocState) { if (!valAllocState.value.equals(orig)) { + if (orig instanceof CastValue castValue && valAllocState.value.equals(castValue.underlyingValue())) { + // check for underlying value for CastValue. + continue; + } + // Kind sizes should be checked here as well. return false; } @@ -604,16 +773,40 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values) { return true; } + protected boolean kindsEqual(Value orig, Value curr) { + var origKind = orig.getValueKind(); + var currKind = curr.getValueKind(); + + if (currKind.equals(origKind)) { + return true; + } + + if (origKind instanceof LIRKindWithCast || currKind instanceof LIRKindWithCast) { + // TestCase: BoxingTest.boxShort + // MOV (x: [v11|QWORD[.] + 12], y: reinterpret: v0|DWORD as: WORD) size: WORD + // MOV (x: [rax|QWORD[.] + 12], y: r10|WORD(DWORD)) size: WORD + // TODO: figure out the correct semantics for these casts + return origKind.getPlatformKind().equals(currKind.getPlatformKind()); + } + + return false; + } + protected boolean checkAliveConstraint(RAVInstruction.Op instruction) { for (int i = 0; i < instruction.alive.count; i++) { + Value value = instruction.alive.curr[i]; + if (Value.ILLEGAL.equals(value)) { + continue; // TODO: remove IllegalValues from these arrays. + } + for (int j = 0; j < instruction.temp.count; j++) { - if (instruction.alive.curr[i].equals(instruction.temp.curr[j])) { + if (value.equals(instruction.temp.curr[j])) { return false; } } for (int j = 0; j < instruction.dests.count; j++) { - if (instruction.alive.curr[i].equals(instruction.dests.curr[j])) { + if (value.equals(instruction.dests.curr[j])) { return false; } } @@ -624,11 +817,13 @@ protected boolean checkAliveConstraint(RAVInstruction.Op instruction) { public boolean check(RAVInstruction.Base instruction) { if (instruction instanceof RAVInstruction.Op op) { - if (!checkInputs(op.uses)) { + boolean isJump = op.lirInstruction instanceof StandardOp.JumpOp; + + if (!checkInputs(op.uses, isJump)) { return false; } - if (!checkInputs(op.alive)) { + if (!checkInputs(op.alive, isJump)) { return false; } @@ -636,7 +831,7 @@ public boolean check(RAVInstruction.Base instruction) { var curr = op.temp.curr[i]; var orig = op.temp.orig[i]; - if (!curr.getValueKind().equals(orig.getValueKind())) { + if (!kindsEqual(orig, curr)) { // Make sure the assigned register has the correct kind for temp. return false; } @@ -650,6 +845,24 @@ public boolean check(RAVInstruction.Base instruction) { return true; } + public String getMissingLabelOrJumpErrMsg(String subject, RAVInstruction.ValueArrayPair values) { + String errorMsg = "["; + for (int j = 0; j < values.count; j++) { + errorMsg += values.orig[j].toString(); + if (values.curr[j] != null) { + errorMsg += " -> " + values.curr[j].toString(); + } else { + errorMsg += " -> ?"; + } + + if (j != values.count-1) { + errorMsg += ", "; + } + } + + return "Failed to resolve: " + subject + " " + errorMsg + "]"; + } + public void update(RAVInstruction.Base instruction) { switch (instruction) { case RAVInstruction.Op op -> { @@ -658,35 +871,76 @@ public void update(RAVInstruction.Base instruction) { continue; // Safe to ignore, when destination is illegal value, not when used. } - if (phiResolution == PhiResolution.FromPredecessors && op.dests.curr[i] == null) { + if ((phiResolution == PhiResolution.FromPredecessors + || phiResolution == PhiResolution.FromUsage) + && op.dests.curr[i] == null) { // This can happen for certain instructions - jump or label, and we need to // resolve appropriate registers for these, if we do not, we throw in check() continue; } // Here, FromJump resolution will fail if it was not completed - // + if (op.dests.curr[i] == null && phiResolution == PhiResolution.FromJump) { + throw new RuntimeException(this.getMissingLabelOrJumpErrMsg("LABEL", op.dests)); + } + assert op.dests.curr[i] != null; assert op.dests.orig[i] != null; + Value location = op.dests.curr[i]; Value variable = op.dests.orig[i]; - this.registerValues.put(op.dests.curr[i], new ValueAllocationState(variable)); + if (ValueUtil.isRegister(location)) { + this.registerValues.put(ValueUtil.asRegister(location), new ValueAllocationState(variable)); + } else { + this.spillSlotValues.put(location, new ValueAllocationState(variable)); + } } for (int i = 0; i < op.temp.count; i++) { + var value = op.temp.curr[i]; + if (Value.ILLEGAL.equals(value)) { + continue; + } + // We cannot believe the contents of registers used as temp, thus we need to reset. - this.registerValues.put(op.temp.curr[i], UnknownAllocationState.INSTANCE); + Value location = op.temp.curr[i]; + if (ValueUtil.isRegister(location)) { + this.registerValues.put(ValueUtil.asRegister(location), UnknownAllocationState.INSTANCE); + } else { + this.spillSlotValues.put(location, UnknownAllocationState.INSTANCE); + } } } case RAVInstruction.Spill spill -> - this.spillSlotValues.putClone(spill.to, this.registerValues.get(spill.from)); + this.spillSlotValues.putClone(spill.to, this.registerValues.get(ValueUtil.asRegister(spill.from))); case RAVInstruction.Reload reload -> - this.registerValues.putClone(reload.to, this.spillSlotValues.get(reload.from)); - case RAVInstruction.Move move -> - this.registerValues.putClone(move.to, this.registerValues.get(move.from)); + this.registerValues.putClone(ValueUtil.asRegister(reload.to), this.spillSlotValues.get(reload.from)); + case RAVInstruction.Move move -> { + var value = this.registerValues.get(ValueUtil.asRegister(move.from)); + if (value.isUnknown()) { + // Hotfix for blockDefinitions, where if we moved a Value we need to make + // sure it's saved in the state, so that value is not propagated further + // causing Circular Exception + // TestCase: ConditionalElimination17 + value = new ValueAllocationState(Value.ILLEGAL); + } + + this.registerValues.putClone(ValueUtil.asRegister(move.to), value); + } + case RAVInstruction.StackMove move -> + this.spillSlotValues.putClone(move.to, this.spillSlotValues.get(move.from)); case RAVInstruction.VirtualMove virtMove -> { if (virtMove.location instanceof RegisterValue) { - this.registerValues.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); + this.registerValues.put(ValueUtil.asRegister(virtMove.location), new ValueAllocationState(virtMove.variableOrConstant)); + } else if (LIRValueUtil.isVariable(virtMove.location)) { + // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD + // Move before allocation + // TODO: maybe handle this better than VirtualMove with location as Variable + // TestCase: BoxingTest.boxBoolean + var locations = this.registerValues.getValueLocations(virtMove.variableOrConstant); + for (var location : locations) { + this.registerValues.put(location, new ValueAllocationState(virtMove.location)); + } } else { this.spillSlotValues.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); } @@ -764,6 +1018,11 @@ public AllocationState clone() { public boolean equals(AllocationState other) { return other.isConflicted(); // TODO: handle contents } + + @Override + public String toString() { + return "Conflicted {" + Arrays.toString(this.conflictedValues.toArray()) + "}"; + } } public static class UnknownAllocationState extends AllocationState { @@ -788,13 +1047,22 @@ public AllocationState clone() { public boolean equals(AllocationState other) { return other == INSTANCE; } + + @Override + public String toString() { + return "Unknown"; + } } public static class ValueAllocationState extends AllocationState implements Cloneable { protected Value value; public ValueAllocationState(Value value) { - if (value instanceof RegisterValue || value instanceof Variable || value instanceof ConstantValue || value instanceof StackSlot) { + if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { + // StackSlot, RegisterValue is present in start block in label as predefined argument + // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS + // ConstantValue act as Variable + // We use variables as symbols for register validation // but real registers can also be used as that, in some cases. // TODO: reconsider handling of StackSlots @@ -838,6 +1106,11 @@ public boolean equals(AllocationState other) { public ValueAllocationState clone() { return new ValueAllocationState(this); } + + @Override + public String toString() { + return "Value {" + this.value + "}"; + } } public interface AllocationStateMap extends Map, Cloneable { @@ -848,20 +1121,32 @@ public interface AllocationStateMap extends Map, Cloneabl boolean mergeWith(AllocationStateMap other); Set getValueLocations(Value value); + + int getTime(K key); } @SuppressWarnings("serial") public class HashAllocationStateMap extends HashMap implements AllocationStateMap { + protected Map locationTimings; + protected int time; + public HashAllocationStateMap() { super(); + + locationTimings = new HashMap<>(); + time = 0; } - public HashAllocationStateMap(AllocationStateMap other) { + public HashAllocationStateMap(HashAllocationStateMap other) { super(other); + + locationTimings = new HashMap<>(other.locationTimings); + time = other.time + 1; } @Override public AllocationState put(K key, AllocationState value) { + locationTimings.put(key, time++); return super.put(key, value); } @@ -887,6 +1172,10 @@ public Set getValueLocations(Value value) { return locations; } + public int getTime(K key) { + return locationTimings.get(key); + } + public AllocationState putClone(K key, AllocationState value) { if (value.isConflicted() || value.isUnknown()) { return this.put(key, value); @@ -916,13 +1205,5 @@ public boolean mergeWith(AllocationStateMap source) { return changed; } - - // TODO: we need to not use size of the work for the hash map for registers at least - private String getCorrectKeyString(Value value) { - return switch (value) { - case RegisterValue reg -> reg.getRegister().toString(); - default -> value.toString(); - }; - } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index e89925a6928e..17a1fa4db313 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -6,6 +6,7 @@ import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.ValueProcedure; import jdk.graal.compiler.lir.Variable; @@ -39,8 +40,33 @@ public static class Options { @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) public static final OptionKey EnableRAVerifier = new OptionKey<>(false); - @Option(help = "", type = OptionType.Debug) + @Option(help = "Select which way you want to resolve phi arguments.", type = OptionType.Debug) public static final EnumOptionKey RAPhiResolution = new EnumOptionKey<>(PhiResolution.FromUsage); + + // @Option(help = "Should constants be moved to variables", type = OptionType.Debug) + // public static final OptionKey MoveConstants = new OptionKey<>(false); + } + + public static String[] ignoredTestCases = { + // Disable Truffle Related Tests + // New instructions being added makes the verifier not work, investigate why prealloc is not run. + "truffle", + "Truffle", + "polyglot", + "Root[]", + "InstrumentationTestLanguage", + "NFITest", + "intCaller1", + "callee" + }; + + public static boolean isIgnored(String compUnitName) { + for (String ignoredTest : ignoredTestCases) { + if (compUnitName.contains(ignoredTest)) { + return true; + } + } + return false; } private PhiResolution phiResolution; @@ -58,8 +84,16 @@ public AllocationPhase getPreAllocPhase() { @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - assert this.preallocPhaseRAVerifier != null : "Phase before register allocation was not run, cannot verify it."; + var compUnitName = lirGenRes.getCompilationUnitName(); + if (RegisterAllocationVerifierPhase.isIgnored(compUnitName)) { + // Skipping truffle test cases because they cause trouble with + // prealloc and getVerifierInstructions, because blocks and + // instructions that aren't made only by the RA are added. + return; + } + + assert this.preallocPhaseRAVerifier != null : "Phase before register allocation was not run, cannot verify it."; var instructions = this.getVerifierInstructions(lirGenRes.getLIR()); var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.phiResolution); @@ -68,7 +102,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo // throw new IllegalStateException("Could not verify the register allocation..."); // Exception handler - System.err.println("Verification failed - " + lirGenRes.getCompilationUnitName()); + System.err.println("Verification failed - " + compUnitName); } } @@ -84,7 +118,7 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) if (instruction.isLoadConstantOp()) { var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); var constant = constatLoad.getConstant(); - var result = (RegisterValue) constatLoad.getResult(); + var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot // This isn't really a virtual move, but it currently acts the same, so we keep it, // we take constants as variables. TODO: maybe remove virtual move altogether for Move(reg, var/constant) @@ -110,6 +144,16 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) return new RAVInstruction.Spill(instruction, stackSlot, reg); } + if (input instanceof StackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof StackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } + return null; } @@ -167,7 +211,7 @@ protected BlockMap> getVerifierInstructions(LIR lir) { return blockInstructions; } - private static class ConstantOverrideValueProcedure implements ValueProcedure { + public static class ConstantOverrideValueProcedure implements ValueProcedure { private LIR lir; private List variables; private Map constantValueMap; @@ -225,6 +269,11 @@ public Map getPreallocMap() { @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + var compUnitName = lirGenRes.getCompilationUnitName(); + if (RegisterAllocationVerifierPhase.isIgnored(compUnitName)) { + return; + } + Map constantValueMap = new HashMap<>(); LIR lir = lirGenRes.getLIR(); @@ -258,7 +307,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); var location = valueMov.getInput(); - var variable = (Variable) valueMov.getResult(); + var variable = LIRValueUtil.asVariable(valueMov.getResult()); var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, location); previousInstr.addVirtualMove(virtualMove); @@ -273,8 +322,8 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo assert previousInstr != null; var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var variable = (Variable) valueMov.getInput(); - var register = (RegisterValue) valueMov.getResult(); + var variable = LIRValueUtil.asVariable(valueMov.getInput()); + var register = valueMov.getResult(); var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); previousInstr.addSpeculativeMove(virtualMove); @@ -297,7 +346,9 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo } var j = instructions.removeLast(); - for (var v : newVars) { + var it = newVars.iterator(); + while (it.hasNext()) { + var v = LIRValueUtil.asVariable(it.next()); var mov = context.spillMoveFactory.createMove(v, constantValueMap.get(v)); var ravInstr = new RAVInstruction.Op(mov); mov.forEachOutput(ravInstr.dests.copyOriginalProc); @@ -325,7 +376,7 @@ protected boolean isVirtualMove(LIRInstruction instruction) { var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); var input = valueMov.getInput(); - return (input instanceof RegisterValue || input instanceof StackSlot) && valueMov.getResult() instanceof Variable; + return (input instanceof RegisterValue || input instanceof StackSlot /*|| input instanceof AbstractAddress*/) && LIRValueUtil.isVariable(valueMov.getResult()); } protected boolean isSpeculativeMove(LIRInstruction instruction) { @@ -334,7 +385,8 @@ protected boolean isSpeculativeMove(LIRInstruction instruction) { } var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - return valueMov.getResult() instanceof RegisterValue && valueMov.getInput() instanceof Variable; + var result = valueMov.getResult(); // Result could be variable or register + return (result instanceof RegisterValue || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); } } } From 69224db71c5d498012bdab0ed31b774de3773cdd Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 5 Jan 2026 22:46:54 +0100 Subject: [PATCH 011/112] Make sure speculative instruction is not missed --- .../alloc/verifier/RegisterAllocationVerifierPhase.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 17a1fa4db313..94138f81061e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -311,10 +311,13 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, location); previousInstr.addVirtualMove(virtualMove); + continue; // No need to store virtual move here, it is stored into previous instruction. } + boolean speculative = false; if (this.isSpeculativeMove(instruction)) { + speculative = true; // Speculative moves are in form ry = MOVE vx, which could be removed if variable // ends up being allocated to the same register as ry. If it was removed // we need to re-add it because it holds important information about where value of @@ -338,7 +341,9 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo this.preallocMap.put(instruction, opRAVInstr); - previousInstr = opRAVInstr; + if (!speculative) { + previousInstr = opRAVInstr; + } } if (newVars.isEmpty()) { From 0775f2a6791362708441c2e113fd1da055e6b09d Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 5 Jan 2026 22:52:39 +0100 Subject: [PATCH 012/112] Resolve more variable locations --- .../verifier/RegisterAllocationVerifier.java | 236 ++++++++++++++---- 1 file changed, 184 insertions(+), 52 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 170630d8ad98..9c039c25e369 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -41,6 +41,7 @@ public final class RegisterAllocationVerifier { // FromPredecessors resolver public BlockMap blockDefinitions; + public BlockMap> blockDefinitions2; public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution) { this.lir = lir; @@ -48,10 +49,8 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); - this.blockEntryStates.put(this.lir.getControlFlowGraph().getStartBlock(), new VerifierState()); - + this.blockEntryStates.put(cfg.getStartBlock(), new VerifierState()); this.blockStates = new BlockMap<>(cfg); - this.phiResolution = phiResolution; this.blockDefinitions = new BlockMap<>(cfg); @@ -300,6 +299,49 @@ private void resolvePhiFromUsage() { // and thus we cannot determine its location. } + public BlockMap> getDefinitionSets() { + BlockMap> blockDefinitions = new BlockMap<>(this.lir.getControlFlowGraph()); + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = blockInstructions.get(block); + Set definitions = new HashSet<>(); + + for (var instruction : instructions) { + switch (instruction) { + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + var location = op.dests.curr[i]; + if (location == null) { + continue; + } + + definitions.add(location); + } + + for (int i = 0; i < op.temp.count; i++) { + var location = op.temp.curr[i]; + if (location == null || Value.ILLEGAL.equals(location)) { + continue; + } + + definitions.add(location); + } + } + case RAVInstruction.Move move -> definitions.add(move.to); + case RAVInstruction.Reload reload -> definitions.add(reload.to); + case RAVInstruction.Spill spill -> definitions.add(spill.to); + case RAVInstruction.StackMove stackMove -> definitions.add(stackMove.to); + case RAVInstruction.VirtualMove virtMove -> definitions.add(virtMove.location); // Is this right? + default -> throw new IllegalStateException(); + } + } + + blockDefinitions.put(block, definitions); + } + + return blockDefinitions; + } + /** * Fill in missing variable locations for current block's label instruction and predecessor * jump instructions. @@ -331,11 +373,38 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O locations.retainAll(varLoc); } + Register location = null; if (locations.size() != 1) { - return false; + if (locations.isEmpty()) { + return false; + } + + for (int j = 0; j < block.getPredecessorCount(); j++) { + int time = -1; + Register blockReg = null; + for (var loc : locations) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + + var regTime = state.registerValues.getTime(loc); + if (regTime > time) { + // TODO: replace time with priority of Moves inserted by the Register Allocator. + time = regTime; // Max time + blockReg = loc; + } + } + + if (location == null) { + location = blockReg; + } else if (location != blockReg) { + // Not same for all blocks, so none choosen. + return false; + } + } + } else { + location = locations.stream().findFirst().get(); } - Register location = locations.stream().findFirst().get(); var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); labelInstr.dests.curr[i] = registerValue; @@ -375,6 +444,13 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { worklist.add(defBlock); propagateMap.put(defBlock, defBlockVariablesToPropagate); + // Monitor_notowner01 + Moniitor_contended01 + // B23 216 stack:48|QWORD[.] = MOVE input: rsi|QWORD[.] moveKind: QWORD // LSRAEliminateSpillMove: store at definition + // old variable supposed to be overwriten stored in spillSpill slots... + // TODO: any location we store the new variables in, needs to be propagated as well + // and this can be done in successors and needs to be accounted for... + // TODO: Reconsider any move with label variable. + while (!worklist.isEmpty()) { var curr = worklist.remove(); if (processed.contains(curr)) { @@ -399,6 +475,10 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { continue; } + if (this.blockDefinitions2.get(curr).contains(register.asValue(variable.getValueKind()))) { + continue; + } + variablesToBePropagated.add(variable); if (state != null) { @@ -422,7 +502,23 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { // for example: B0 defines [v0] in label, B1 is it's successor as well as it's // predecessor, and if it does not overwrite this register, it would change // entry state for B0 to include v0, which is defined by B0. - throw new GraalError("Circular definition for variable detected."); + + String errorMsg = "["; + var values = labelInstr.dests; + for (int j = 0; j < values.count; j++) { + errorMsg += values.orig[j].toString(); + if (values.curr[j] != null) { + errorMsg += " -> " + values.curr[j].toString(); + } else { + errorMsg += " -> ?"; + } + + if (j != values.count - 1) { + errorMsg += ", "; + } + } + + throw new GraalError("Circular definition for variable detected " + curr + " -> " + defBlock + " on LABEL " + errorMsg + "]"); } boolean dominates = succ.dominates(defBlock); @@ -458,53 +554,87 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { private void resolvePhiFromJump(BasicBlock block) { var state = this.blockStates.get(block); var jumpInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getLast(); + for (int i = 0; i < jumpInstr.alive.count; i++) { if (jumpInstr.alive.curr[i] != null) { continue; } var inputValue = jumpInstr.alive.orig[i]; - if (LIRValueUtil.isVariable(inputValue) || inputValue instanceof ConstantValue) { - // Does not work for constants if there is more of them - var locations = state.registerValues.getValueLocations(inputValue); - if (locations.isEmpty()) { - continue; + if (!LIRValueUtil.isVariable(inputValue) && !(inputValue instanceof ConstantValue)) { + continue; + } + + var registerValue = this.getRegisterBeforeJump(state, inputValue); + if (registerValue == null) { + registerValue = this.getStackSlotBeforeJump(state, inputValue); + if (registerValue == null) { + return; } + } - var register = locations.stream().findFirst().get(); - if (locations.size() != 1) { - int time = -1; - for (var location : locations) { - var locTime = state.registerValues.getTime(location); - if (locTime > time) { - register = location; - time = locTime; - } + jumpInstr.alive.curr[i] = registerValue; + for (int j = 0; j < block.getSuccessorCount(); j++) { + var succ = block.getSuccessorAt(j); + var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); + + labelInstr.dests.curr[i] = jumpInstr.alive.curr[i]; + + for (int k = 0; k < succ.getPredecessorCount(); k++) { + // Sibling jumps need to be updated as well in order to pass input checks. + var sibling = succ.getPredecessorAt(k); + if (sibling.equals(block)) { + continue; } + + var siblingJumpInstr = (RAVInstruction.Op) this.blockInstructions.get(sibling).getLast(); + siblingJumpInstr.alive.curr[i] = jumpInstr.alive.curr[i]; } + } + } + } - var registerValue = register.asValue(jumpInstr.alive.orig[i].getValueKind()); - jumpInstr.alive.curr[i] = registerValue; + private Value getRegisterBeforeJump(VerifierState state, Value inputValue) { + // Does not work for constants if there is more of them + var locations = state.registerValues.getValueLocations(inputValue); + if (locations.isEmpty()) { + return null; + } - for (int j = 0; j < block.getSuccessorCount(); j++) { - var succ = block.getSuccessorAt(j); - var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); + var register = locations.stream().findFirst().get(); + if (locations.size() != 1) { + int time = -1; + for (var location : locations) { + var locTime = state.registerValues.getTime(location); + if (locTime > time) { + register = location; + time = locTime; + } + } + } - labelInstr.dests.curr[i] = jumpInstr.alive.curr[i]; + return register.asValue(inputValue.getValueKind()); + } - for (int k = 0; k < succ.getPredecessorCount(); k++) { - // Sibling jumps need to be updated as well in order to pass input checks. - var sibling = succ.getPredecessorAt(k); - if (sibling.equals(block)) { - continue; - } + private Value getStackSlotBeforeJump(VerifierState state, Value inputValue) { + var locations = state.spillSlotValues.getValueLocations(inputValue); + if (locations.isEmpty()) { + return null; + } - var siblingJumpInstr = (RAVInstruction.Op) this.blockInstructions.get(sibling).getLast(); - siblingJumpInstr.alive.curr[i] = jumpInstr.alive.curr[i]; - } + var spillSlot = locations.stream().findFirst().get(); + if (locations.size() != 1) { + int time = -1; + for (var location : locations) { + var locTime = state.spillSlotValues.getTime(location); + if (locTime > time) { + spillSlot = location; + time = locTime; } } } + + return spillSlot; } private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { @@ -596,7 +726,7 @@ public void calculateEntryBlocks() { * This method needs to be run, because sometimes already processed blocks * do not get their labels updated with newly defined variables and thus * resolution is not complete. - * + *

* TODO: this might be better be handled by some dependency graph * * @param worklist Worklist to which we add new blocks for processing. @@ -638,8 +768,8 @@ private void addMissingLabelBlocks(Queue> worklist) { * @return true, if valid, otherwise false */ public boolean verifyInstructionInputs() { - for (int i = 0; i < this.lir.getBlocks().length; i++) { - var block = this.lir.getBlocks()[i]; + for (var blockId : this.lir.getBlocks()) { + var block = this.lir.getBlockById(blockId); var state = this.blockEntryStates.get(block); var instructions = this.blockInstructions.get(block); @@ -672,6 +802,8 @@ public boolean run() { } this.blockDefinitions.put(block, state); } + + this.blockDefinitions2 = this.getDefinitionSets(); } this.calculateEntryBlocks(); @@ -855,7 +987,7 @@ public String getMissingLabelOrJumpErrMsg(String subject, RAVInstruction.ValueAr errorMsg += " -> ?"; } - if (j != values.count-1) { + if (j != values.count - 1) { errorMsg += ", "; } } @@ -872,8 +1004,8 @@ public void update(RAVInstruction.Base instruction) { } if ((phiResolution == PhiResolution.FromPredecessors - || phiResolution == PhiResolution.FromUsage) - && op.dests.curr[i] == null) { + || phiResolution == PhiResolution.FromUsage) + && op.dests.curr[i] == null) { // This can happen for certain instructions - jump or label, and we need to // resolve appropriate registers for these, if we do not, we throw in check() continue; @@ -916,19 +1048,19 @@ public void update(RAVInstruction.Base instruction) { case RAVInstruction.Reload reload -> this.registerValues.putClone(ValueUtil.asRegister(reload.to), this.spillSlotValues.get(reload.from)); case RAVInstruction.Move move -> { - var value = this.registerValues.get(ValueUtil.asRegister(move.from)); - if (value.isUnknown()) { - // Hotfix for blockDefinitions, where if we moved a Value we need to make - // sure it's saved in the state, so that value is not propagated further - // causing Circular Exception - // TestCase: ConditionalElimination17 - value = new ValueAllocationState(Value.ILLEGAL); - } + var value = this.registerValues.get(ValueUtil.asRegister(move.from)); + if (value.isUnknown()) { + // Hotfix for blockDefinitions, where if we moved a Value we need to make + // sure it's saved in the state, so that value is not propagated further + // causing Circular Exception + // TestCase: ConditionalElimination17 + value = new ValueAllocationState(Value.ILLEGAL); + } - this.registerValues.putClone(ValueUtil.asRegister(move.to), value); + this.registerValues.putClone(ValueUtil.asRegister(move.to), value); } case RAVInstruction.StackMove move -> - this.spillSlotValues.putClone(move.to, this.spillSlotValues.get(move.from)); + this.spillSlotValues.putClone(move.to, this.spillSlotValues.get(move.from)); case RAVInstruction.VirtualMove virtMove -> { if (virtMove.location instanceof RegisterValue) { this.registerValues.put(ValueUtil.asRegister(virtMove.location), new ValueAllocationState(virtMove.variableOrConstant)); @@ -1126,7 +1258,7 @@ public interface AllocationStateMap extends Map, Cloneabl } @SuppressWarnings("serial") - public class HashAllocationStateMap extends HashMap implements AllocationStateMap { + public static class HashAllocationStateMap extends HashMap implements AllocationStateMap { protected Map locationTimings; protected int time; From 15eb8a44bfa7cec9c34233755b36e1e19497dbbb Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 6 Jan 2026 14:41:23 +0100 Subject: [PATCH 013/112] Store all locations in one Map --- .../lir/alloc/verifier/AllocationState.java | 21 + .../verifier/ConflictedAllocationState.java | 62 ++ .../verifier/MergedAllocationStateMap.java | 196 ++++++ .../verifier/MergedBlockVerifierState.java | 262 ++++++++ .../verifier/RegisterAllocationVerifier.java | 620 +----------------- .../verifier/UnknownAllocationState.java | 30 + .../alloc/verifier/ValueAllocationState.java | 67 ++ 7 files changed, 669 insertions(+), 589 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java new file mode 100644 index 000000000000..d786dbed438e --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -0,0 +1,21 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +public abstract class AllocationState { + public static AllocationState getDefault() { + return UnknownAllocationState.INSTANCE; + } + + public boolean isUnknown() { + return false; + } + + public boolean isConflicted() { + return false; + } + + public abstract AllocationState clone(); + + public abstract AllocationState meet(AllocationState other); + + public abstract boolean equals(AllocationState other); +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java new file mode 100644 index 000000000000..f31df6a21065 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -0,0 +1,62 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.vm.ci.meta.Value; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class ConflictedAllocationState extends AllocationState { + protected Set conflictedValues; + + public ConflictedAllocationState(Value value1, Value value2) { + this.conflictedValues = new HashSet<>(); + this.conflictedValues.add(value1); + this.conflictedValues.add(value2); + } + + private ConflictedAllocationState(Set conflictedValues) { + this.conflictedValues = new HashSet<>(conflictedValues); + } + + public void addConflictedValue(Value value) { + this.conflictedValues.add(value); + } + + public Set getConflictedValues() { + return this.conflictedValues; + } + + @Override + public boolean isConflicted() { + return true; + } + + @Override + public AllocationState meet(AllocationState other) { + if (other instanceof ValueAllocationState valueState) { + this.addConflictedValue(valueState.getValue()); + } + + if (other instanceof ConflictedAllocationState conflictedState) { + this.conflictedValues.addAll(conflictedState.conflictedValues); + } + + return this; + } + + @Override + public AllocationState clone() { + return new ConflictedAllocationState(this.conflictedValues); + } + + @Override + public boolean equals(AllocationState other) { + return other.isConflicted(); // TODO: handle contents + } + + @Override + public String toString() { + return "Conflicted {" + Arrays.toString(this.conflictedValues.toArray()) + "}"; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java new file mode 100644 index 000000000000..e9ef78ae6d6a --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -0,0 +1,196 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class MergedAllocationStateMap { + /** + * These are instances of Value we need to keep for getValueLocations, + * indexed by their string representation. + * + * Because Values have their own toString implementation, + * that the hash map uses and for some Values we do not + * want this (especially due to kinds), which are irrelevant + * for location indices, we have a getValueKeyString which + * does what we need. + */ + protected Map valueMap; + protected Map internalMap; + protected Map locationTimings; + /** + * Prioritized locations are ones made by the register allocator itself. + * + * Whenever we are resolving phi variables, these are prioritized + * because they are likely what the register allocator chose + * to be used as phi locations. + * + * If there's multiple, then time should make the difference. + */ + protected Map prioritizedLocations; + protected int time; + + // The idea behind this is that we do not care where the address is going + // we care about if we ever saw this in memory, then we return ValueAllocationState + // CompositeValue AMD64AddressValue&AArch64AddressValue + protected Set inMemoryValues; + + protected class ValueWrapper { + protected Value value; + + protected ValueWrapper(Value value) { + this.value = value; + } + + protected Value getValue() { + return this.value; + } + + @Override + public String toString() { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + return value.toString(); + } + } + + public MergedAllocationStateMap() { + valueMap = new HashMap<>(); + internalMap = new HashMap<>(); + locationTimings = new HashMap<>(); + prioritizedLocations = new HashMap<>(); + time = 0; + inMemoryValues = new HashSet<>(); + } + + public MergedAllocationStateMap(MergedAllocationStateMap other) { + valueMap = new HashMap<>(other.valueMap); + internalMap = new HashMap<>(other.internalMap); + locationTimings = new HashMap<>(other.locationTimings); + prioritizedLocations = new HashMap<>(other.prioritizedLocations); + time = other.time + 1; + inMemoryValues = new HashSet<>(other.inMemoryValues); + } + + public AllocationState get(Value key) { + String keyString = this.getValueKeyString(key); + return internalMap.get(keyString); + } + + public AllocationState get(Value key, AllocationState defaultValue) { + String keyString = this.getValueKeyString(key); + var state = internalMap.get(keyString); + if (state == null) { + return defaultValue; + } + return state; + } + + public AllocationState get(Register register) { + return this.get(register.asValue()); + } + + public void putAsPrioritized(Value key, AllocationState value) { + this.put(key, value); + this.prioritizedLocations.put(this.getValueKeyString(key), true); + } + + public void put(Register reg, AllocationState value) { + this.put(reg.asValue(), value); + } + + public void put(Value key, AllocationState state) { + String keyString = this.getValueKeyString(key); + locationTimings.put(keyString, time++); + internalMap.put(keyString, state); + valueMap.put(keyString, key); + + if (prioritizedLocations.containsKey(keyString)) { + prioritizedLocations.put(keyString, false); + } + } + + public void putClone(Value key, AllocationState value) { + if (value.isConflicted() || value.isUnknown()) { + this.put(key, value); + return; + } + + this.put(key, value.clone()); + } + + public void putClone(Register reg, AllocationState value) { + this.put(reg.asValue(), value.clone()); + } + + public void putCloneAsPrioritized(Value key, AllocationState value) { + this.putClone(key, value); + this.prioritizedLocations.put(this.getValueKeyString(key), true); + } + + public boolean isPrioritized(Value key) { + String keyString = this.getValueKeyString(key); + return prioritizedLocations.containsKey(keyString); + } + + public int getKeyTime(Value key) { + String keyString = this.getValueKeyString(key); + var time = locationTimings.get(keyString); + assert time != null : "Time for key " + keyString + " not present."; + return time; + } + + public Set getValueLocations(Value value) { + // TODO: maybe we can just store these in a hash map as well? + + Set locations = new HashSet<>(); + for (var entry : this.internalMap.entrySet()) { + if (entry.getValue() instanceof ValueAllocationState valState) { + if (valState.getValue().equals(value)) { + var location = this.valueMap.get(entry.getKey()); + assert location != null : "Value not present in ValueMap: " + entry.getKey(); + locations.add(location); + } + } + } + return locations; + } + + public boolean mergeWith(MergedAllocationStateMap source) { + boolean changed = false; + for (var entry : source.internalMap.entrySet()) { + if (!this.internalMap.containsKey(entry.getKey())) { + changed = true; + + this.put(source.valueMap.get(entry.getKey()), UnknownAllocationState.INSTANCE); + } + + var currentValue = this.internalMap.get(entry.getKey()); + var result = this.internalMap.get(entry.getKey()).meet(entry.getValue()); + if (!currentValue.equals(result)) { + changed = true; + } + + this.put(source.valueMap.get(entry.getKey()), result); + } + + return changed; + } + + protected String getValueKeyString(Value value) { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + assert !Value.ILLEGAL.equals(value) : "Cannot use ILLEGAL as key in AllocationStateMap"; + + return value.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java new file mode 100644 index 000000000000..a55b6e6a79e1 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -0,0 +1,262 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.LIRKindWithCast; +import jdk.graal.compiler.lir.CastValue; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +public class MergedBlockVerifierState { + public MergedAllocationStateMap values; + + protected PhiResolution phiResolution; + + public MergedBlockVerifierState(PhiResolution phiResolution) { + this.values = new MergedAllocationStateMap(); + this.phiResolution = phiResolution; + } + + protected MergedBlockVerifierState(MergedBlockVerifierState other, PhiResolution phiResolution) { + this.phiResolution = phiResolution; + + if (other == null) { + this.values = new MergedAllocationStateMap(); + return; + } + + this.values = new MergedAllocationStateMap(other.values); + } + + public MergedAllocationStateMap getValues() { + return values; + } + + public boolean meetWith(MergedBlockVerifierState other) { + return this.values.mergeWith(other.getValues()); + } + + protected boolean checkInputs(RAVInstruction.ValueArrayPair values, boolean isJump) { + // Check that incoming values are not unknown or conflicted - these only matter if used + for (int idx = 0; idx < values.count; idx++) { + var orig = values.orig[idx]; + var curr = values.curr[idx]; + + if (curr == null && isJump && phiResolution == PhiResolution.FromUsage) { + // Whenever PhiResolution = FromUsage, variable is not used and thus no register present. + continue; + } + + assert orig != null; + + if (curr == null) { + if (isJump) { + throw new RuntimeException(this.getMissingLabelOrJumpErrMsg("JUMP", values)); + } + + assert false; + } + + if (orig.equals(curr)) { + // In this case nothing has changed so we have nothing to verify + continue; + } + + AllocationState state = this.values.get(curr); + + if (!kindsEqual(orig, curr)) { + return false; + } + + if (state.isConflicted() || state.isUnknown()) { + return false; + } + + if (state instanceof ValueAllocationState valAllocState) { + if (!valAllocState.value.equals(orig)) { + if (orig instanceof CastValue castValue && valAllocState.value.equals(castValue.underlyingValue())) { + // check for underlying value for CastValue. + continue; + } + + // Kind sizes should be checked here as well. + return false; + } + + continue; + } + + throw new IllegalStateException(); // Should never reach here. + } + + return true; + } + + protected boolean kindsEqual(Value orig, Value curr) { + var origKind = orig.getValueKind(); + var currKind = curr.getValueKind(); + + if (currKind.equals(origKind)) { + return true; + } + + if (origKind instanceof LIRKindWithCast || currKind instanceof LIRKindWithCast) { + // TestCase: BoxingTest.boxShort + // MOV (x: [v11|QWORD[.] + 12], y: reinterpret: v0|DWORD as: WORD) size: WORD + // MOV (x: [rax|QWORD[.] + 12], y: r10|WORD(DWORD)) size: WORD + // TODO: figure out the correct semantics for these casts + return origKind.getPlatformKind().equals(currKind.getPlatformKind()); + } + + return false; + } + + protected boolean checkAliveConstraint(RAVInstruction.Op instruction) { + for (int i = 0; i < instruction.alive.count; i++) { + Value value = instruction.alive.curr[i]; + if (Value.ILLEGAL.equals(value)) { + continue; // TODO: remove IllegalValues from these arrays. + } + + for (int j = 0; j < instruction.temp.count; j++) { + if (value.equals(instruction.temp.curr[j])) { + return false; + } + } + + for (int j = 0; j < instruction.dests.count; j++) { + if (value.equals(instruction.dests.curr[j])) { + return false; + } + } + } + + return true; + } + + public boolean check(RAVInstruction.Base instruction) { + if (instruction instanceof RAVInstruction.Op op) { + boolean isJump = op.lirInstruction instanceof StandardOp.JumpOp; + + if (!checkInputs(op.uses, isJump)) { + return false; + } + + if (!checkInputs(op.alive, isJump)) { + return false; + } + + for (int i = 0; i < op.temp.count; i++) { + var curr = op.temp.curr[i]; + var orig = op.temp.orig[i]; + + if (!kindsEqual(orig, curr)) { + // Make sure the assigned register has the correct kind for temp. + return false; + } + } + + if (!this.checkAliveConstraint(op)) { + return false; + } + } + + return true; + } + + public String getMissingLabelOrJumpErrMsg(String subject, RAVInstruction.ValueArrayPair values) { + String errorMsg = "["; + for (int j = 0; j < values.count; j++) { + errorMsg += values.orig[j].toString(); + if (values.curr[j] != null) { + errorMsg += " -> " + values.curr[j].toString(); + } else { + errorMsg += " -> ?"; + } + + if (j != values.count - 1) { + errorMsg += ", "; + } + } + + return "Failed to resolve: " + subject + " " + errorMsg + "]"; + } + + public void update(RAVInstruction.Base instruction) { + switch (instruction) { + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + if (Value.ILLEGAL.equals(op.dests.orig[i])) { + continue; // Safe to ignore, when destination is illegal value, not when used. + } + + if ((phiResolution == PhiResolution.FromPredecessors + || phiResolution == PhiResolution.FromUsage) + && op.dests.curr[i] == null) { + // This can happen for certain instructions - jump or label, and we need to + // resolve appropriate registers for these, if we do not, we throw in check() + continue; + } + + // Here, FromJump resolution will fail if it was not completed + if (op.dests.curr[i] == null && phiResolution == PhiResolution.FromJump) { + throw new RuntimeException(this.getMissingLabelOrJumpErrMsg("LABEL", op.dests)); + } + + assert op.dests.curr[i] != null; + assert op.dests.orig[i] != null; + + Value location = op.dests.curr[i]; + Value variable = op.dests.orig[i]; + this.values.put(location, new ValueAllocationState(variable)); + } + + for (int i = 0; i < op.temp.count; i++) { + var value = op.temp.curr[i]; + if (Value.ILLEGAL.equals(value)) { + continue; + } + + // We cannot believe the contents of registers used as temp, thus we need to reset. + Value location = op.temp.curr[i]; + this.values.put(location, UnknownAllocationState.INSTANCE); + } + } + case RAVInstruction.Spill spill -> + this.values.putClone(spill.to, this.values.get(spill.from)); + case RAVInstruction.Reload reload -> + this.values.putClone(reload.to, this.values.get(reload.from)); + case RAVInstruction.Move move -> { + var value = this.values.get(move.from); + if (value.isUnknown()) { + // Hotfix for blockDefinitions, where if we moved a Value we need to make + // sure it's saved in the state, so that value is not propagated further + // causing Circular Exception + // TestCase: ConditionalElimination17 + value = new ValueAllocationState(Value.ILLEGAL); + } + + this.values.putClone(move.to, value); + } + case RAVInstruction.StackMove move -> + this.values.putClone(move.to, this.values.get(move.from)); + case RAVInstruction.VirtualMove virtMove -> { + if (virtMove.location instanceof RegisterValue) { + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); + } else if (LIRValueUtil.isVariable(virtMove.location)) { + // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD + // Move before allocation + // TODO: maybe handle this better than VirtualMove with location as Variable + // TestCase: BoxingTest.boxBoolean + var locations = this.values.getValueLocations(virtMove.variableOrConstant); + for (var location : locations) { + this.values.put(location, new ValueAllocationState(virtMove.location)); + } + } else { + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); + } + } + default -> throw new IllegalStateException(); + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 9c039c25e369..dda5ef07af4d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -1,23 +1,16 @@ package jdk.graal.compiler.lir.alloc.verifier; -import jdk.graal.compiler.core.common.LIRKindWithCast; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.CastValue; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; -import jdk.graal.compiler.lir.VirtualStackSlot; -import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -28,8 +21,8 @@ public final class RegisterAllocationVerifier { public BlockMap> blockInstructions; - public BlockMap blockStates; // Current states - public BlockMap blockEntryStates; // State on entry to block + public BlockMap blockStates; // Current states + public BlockMap blockEntryStates; // State on entry to block public LIR lir; public PhiResolution phiResolution; @@ -40,7 +33,7 @@ public final class RegisterAllocationVerifier { public Map usageAliasMap; // FromPredecessors resolver - public BlockMap blockDefinitions; + public BlockMap blockDefinitions; public BlockMap> blockDefinitions2; public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution) { @@ -49,7 +42,7 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); - this.blockEntryStates.put(cfg.getStartBlock(), new VerifierState()); + this.blockEntryStates.put(cfg.getStartBlock(), new MergedBlockVerifierState(phiResolution)); this.blockStates = new BlockMap<>(cfg); this.phiResolution = phiResolution; @@ -356,14 +349,14 @@ public BlockMap> getDefinitionSets() { */ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; + Set locations = null; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); var state = this.blockStates.get(pred); var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); var inputValue = jump.alive.orig[i]; - var varLoc = state.registerValues.getValueLocations(inputValue); + var varLoc = state.values.getValueLocations(inputValue); if (locations == null) { locations = varLoc; continue; @@ -373,7 +366,7 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O locations.retainAll(varLoc); } - Register location = null; + Value location = null; if (locations.size() != 1) { if (locations.isEmpty()) { return false; @@ -381,12 +374,12 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O for (int j = 0; j < block.getPredecessorCount(); j++) { int time = -1; - Register blockReg = null; + Value blockReg = null; for (var loc : locations) { var pred = block.getPredecessorAt(j); var state = this.blockStates.get(pred); - var regTime = state.registerValues.getTime(loc); + var regTime = state.values.getKeyTime(loc); if (regTime > time) { // TODO: replace time with priority of Moves inserted by the Register Allocator. time = regTime; // Max time @@ -396,7 +389,7 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O if (location == null) { location = blockReg; - } else if (location != blockReg) { + } else if (location.equals(blockReg)) { // Not same for all blocks, so none choosen. return false; } @@ -405,7 +398,8 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O location = locations.stream().findFirst().get(); } - var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); + var registerValue = location; + // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); labelInstr.dests.curr[i] = registerValue; for (int j = 0; j < block.getPredecessorCount(); j++) { @@ -423,20 +417,20 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { // Definition block needs to have this set. var propagateMap = new HashMap, List>(); - var variableToRegisters = new HashMap(); + var variableToRegisters = new HashMap(); var defBlockVariablesToPropagate = new LinkedList(); var defForEntry = this.blockDefinitions.get(defBlock); for (int i = 0; i < labelInstr.dests.count; i++) { - var register = ValueUtil.asRegister(labelInstr.dests.curr[i]); + var register = (RegisterValue) labelInstr.dests.curr[i]; var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - var registerDefinition = defForEntry.registerValues.get(register); + var registerDefinition = defForEntry.values.get(register); if (registerDefinition.isUnknown()) { - defForEntry.registerValues.put(register, new ValueAllocationState(variable)); + defForEntry.values.put(register, new ValueAllocationState(variable)); } defBlockVariablesToPropagate.add(variable); - variableToRegisters.put(variable, register); + variableToRegisters.put(variable, register); } Queue> worklist = new LinkedList<>(); @@ -468,21 +462,21 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var variable = LIRValueUtil.asVariable(itToPropagate.next()); var register = variableToRegisters.get(variable); - var registerDefinition = def.registerValues.get(register); + var registerDefinition = def.values.get(register); if (!registerDefinition.isUnknown() && registerDefinition instanceof ValueAllocationState valState && !valState.getValue().equals(variable)) { // This block has redefined the value of said register, // and we will not pass it further. continue; } - if (this.blockDefinitions2.get(curr).contains(register.asValue(variable.getValueKind()))) { + if (this.blockDefinitions2.get(curr).contains(register)) { continue; } variablesToBePropagated.add(variable); if (state != null) { - state.registerValues.put(register, new ValueAllocationState(variable)); + state.values.put(register, new ValueAllocationState(variable)); } } @@ -530,7 +524,7 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { while (itToBePropagated.hasNext()) { var variable = LIRValueUtil.asVariable(itToBePropagated.next()); var register = variableToRegisters.get(variable); - succEntryState.registerValues.put(register, new ValueAllocationState(variable)); + succEntryState.values.put(register, new ValueAllocationState(variable)); } propagateMap.put(succ, variablesToBePropagated); @@ -567,10 +561,7 @@ private void resolvePhiFromJump(BasicBlock block) { var registerValue = this.getRegisterBeforeJump(state, inputValue); if (registerValue == null) { - registerValue = this.getStackSlotBeforeJump(state, inputValue); - if (registerValue == null) { - return; - } + return; } jumpInstr.alive.curr[i] = registerValue; @@ -594,9 +585,9 @@ private void resolvePhiFromJump(BasicBlock block) { } } - private Value getRegisterBeforeJump(VerifierState state, Value inputValue) { + private Value getRegisterBeforeJump(MergedBlockVerifierState state, Value inputValue) { // Does not work for constants if there is more of them - var locations = state.registerValues.getValueLocations(inputValue); + var locations = state.values.getValueLocations(inputValue); if (locations.isEmpty()) { return null; } @@ -605,7 +596,7 @@ private Value getRegisterBeforeJump(VerifierState state, Value inputValue) { if (locations.size() != 1) { int time = -1; for (var location : locations) { - var locTime = state.registerValues.getTime(location); + var locTime = state.values.getKeyTime(location); if (locTime > time) { register = location; time = locTime; @@ -613,28 +604,8 @@ private Value getRegisterBeforeJump(VerifierState state, Value inputValue) { } } - return register.asValue(inputValue.getValueKind()); - } - - private Value getStackSlotBeforeJump(VerifierState state, Value inputValue) { - var locations = state.spillSlotValues.getValueLocations(inputValue); - if (locations.isEmpty()) { - return null; - } - - var spillSlot = locations.stream().findFirst().get(); - if (locations.size() != 1) { - int time = -1; - for (var location : locations) { - var locTime = state.spillSlotValues.getTime(location); - if (locTime > time) { - spillSlot = location; - time = locTime; - } - } - } - - return spillSlot; + return register; + // return register.asValue(inputValue.getValueKind()); } private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { @@ -670,7 +641,7 @@ public void calculateEntryBlocks() { } // Create new entry state for successor blocks out of current block state - var state = new VerifierState(this.blockEntryStates.get(block)); + var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), phiResolution); for (var instr : instructions) { state.update(instr); } @@ -687,9 +658,9 @@ public void calculateEntryBlocks() { for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); - VerifierState succState; + MergedBlockVerifierState succState; if (this.blockEntryStates.get(succ) == null) { - succState = new VerifierState(); + succState = new MergedBlockVerifierState(phiResolution); // Either there's no state because it was not yet processed first part of the condition // or, we need to reset it because label changed, second part of the condition @@ -796,7 +767,7 @@ public boolean run() { for (var blockId : this.lir.getBlocks()) { var block = this.lir.getBlockById(blockId); var instructions = this.blockInstructions.get(block); - var state = new VerifierState(); + var state = new MergedBlockVerifierState(phiResolution); for (var instr : instructions) { state.update(instr); } @@ -809,533 +780,4 @@ public boolean run() { this.calculateEntryBlocks(); return this.verifyInstructionInputs(); } - - public class VerifierState { - protected HashAllocationStateMap registerValues; - protected HashAllocationStateMap spillSlotValues; - - public VerifierState() { - registerValues = new HashAllocationStateMap<>(); - spillSlotValues = new HashAllocationStateMap<>(); - } - - protected VerifierState(VerifierState other) { - if (other == null) { - registerValues = new HashAllocationStateMap<>(); - spillSlotValues = new HashAllocationStateMap<>(); - return; - } - - registerValues = new HashAllocationStateMap<>(other.getRegisterValues()); - spillSlotValues = new HashAllocationStateMap<>(other.getSpillSlotValues()); - } - - public HashAllocationStateMap getRegisterValues() { - return registerValues; - } - - public HashAllocationStateMap getSpillSlotValues() { - return spillSlotValues; - } - - public boolean meetWith(VerifierState other) { - var regChanged = this.registerValues.mergeWith(other.getRegisterValues()); - var stackChanged = this.spillSlotValues.mergeWith(other.getSpillSlotValues()); - return regChanged || stackChanged; - } - - protected boolean checkInputs(RAVInstruction.ValueArrayPair values, boolean isJump) { - // Check that incoming values are not unknown or conflicted - these only matter if used - for (int idx = 0; idx < values.count; idx++) { - var orig = values.orig[idx]; - var curr = values.curr[idx]; - - if (curr == null && isJump && phiResolution == PhiResolution.FromUsage) { - // Whenever PhiResolution = FromUsage, variable is not used and thus no register present. - continue; - } - - assert orig != null; - - if (curr == null) { - if (isJump) { - throw new RuntimeException(this.getMissingLabelOrJumpErrMsg("JUMP", values)); - } - - assert false; - } - - if (orig.equals(curr)) { - // In this case nothing has changed so we have nothing to verify - continue; - } - - AllocationState state; - if (curr instanceof RegisterValue) { - state = this.registerValues.get(ValueUtil.asRegister(curr)); - } else { - state = this.spillSlotValues.get(curr); - } - - if (!kindsEqual(orig, curr)) { - return false; - } - - if (state.isConflicted() || state.isUnknown()) { - return false; - } - - if (state instanceof ValueAllocationState valAllocState) { - if (!valAllocState.value.equals(orig)) { - if (orig instanceof CastValue castValue && valAllocState.value.equals(castValue.underlyingValue())) { - // check for underlying value for CastValue. - continue; - } - - // Kind sizes should be checked here as well. - return false; - } - - continue; - } - - throw new IllegalStateException(); // Should never reach here. - } - - return true; - } - - protected boolean kindsEqual(Value orig, Value curr) { - var origKind = orig.getValueKind(); - var currKind = curr.getValueKind(); - - if (currKind.equals(origKind)) { - return true; - } - - if (origKind instanceof LIRKindWithCast || currKind instanceof LIRKindWithCast) { - // TestCase: BoxingTest.boxShort - // MOV (x: [v11|QWORD[.] + 12], y: reinterpret: v0|DWORD as: WORD) size: WORD - // MOV (x: [rax|QWORD[.] + 12], y: r10|WORD(DWORD)) size: WORD - // TODO: figure out the correct semantics for these casts - return origKind.getPlatformKind().equals(currKind.getPlatformKind()); - } - - return false; - } - - protected boolean checkAliveConstraint(RAVInstruction.Op instruction) { - for (int i = 0; i < instruction.alive.count; i++) { - Value value = instruction.alive.curr[i]; - if (Value.ILLEGAL.equals(value)) { - continue; // TODO: remove IllegalValues from these arrays. - } - - for (int j = 0; j < instruction.temp.count; j++) { - if (value.equals(instruction.temp.curr[j])) { - return false; - } - } - - for (int j = 0; j < instruction.dests.count; j++) { - if (value.equals(instruction.dests.curr[j])) { - return false; - } - } - } - - return true; - } - - public boolean check(RAVInstruction.Base instruction) { - if (instruction instanceof RAVInstruction.Op op) { - boolean isJump = op.lirInstruction instanceof StandardOp.JumpOp; - - if (!checkInputs(op.uses, isJump)) { - return false; - } - - if (!checkInputs(op.alive, isJump)) { - return false; - } - - for (int i = 0; i < op.temp.count; i++) { - var curr = op.temp.curr[i]; - var orig = op.temp.orig[i]; - - if (!kindsEqual(orig, curr)) { - // Make sure the assigned register has the correct kind for temp. - return false; - } - } - - if (!this.checkAliveConstraint(op)) { - return false; - } - } - - return true; - } - - public String getMissingLabelOrJumpErrMsg(String subject, RAVInstruction.ValueArrayPair values) { - String errorMsg = "["; - for (int j = 0; j < values.count; j++) { - errorMsg += values.orig[j].toString(); - if (values.curr[j] != null) { - errorMsg += " -> " + values.curr[j].toString(); - } else { - errorMsg += " -> ?"; - } - - if (j != values.count - 1) { - errorMsg += ", "; - } - } - - return "Failed to resolve: " + subject + " " + errorMsg + "]"; - } - - public void update(RAVInstruction.Base instruction) { - switch (instruction) { - case RAVInstruction.Op op -> { - for (int i = 0; i < op.dests.count; i++) { - if (Value.ILLEGAL.equals(op.dests.orig[i])) { - continue; // Safe to ignore, when destination is illegal value, not when used. - } - - if ((phiResolution == PhiResolution.FromPredecessors - || phiResolution == PhiResolution.FromUsage) - && op.dests.curr[i] == null) { - // This can happen for certain instructions - jump or label, and we need to - // resolve appropriate registers for these, if we do not, we throw in check() - continue; - } - - // Here, FromJump resolution will fail if it was not completed - if (op.dests.curr[i] == null && phiResolution == PhiResolution.FromJump) { - throw new RuntimeException(this.getMissingLabelOrJumpErrMsg("LABEL", op.dests)); - } - - assert op.dests.curr[i] != null; - assert op.dests.orig[i] != null; - - Value location = op.dests.curr[i]; - Value variable = op.dests.orig[i]; - if (ValueUtil.isRegister(location)) { - this.registerValues.put(ValueUtil.asRegister(location), new ValueAllocationState(variable)); - } else { - this.spillSlotValues.put(location, new ValueAllocationState(variable)); - } - } - - for (int i = 0; i < op.temp.count; i++) { - var value = op.temp.curr[i]; - if (Value.ILLEGAL.equals(value)) { - continue; - } - - // We cannot believe the contents of registers used as temp, thus we need to reset. - Value location = op.temp.curr[i]; - if (ValueUtil.isRegister(location)) { - this.registerValues.put(ValueUtil.asRegister(location), UnknownAllocationState.INSTANCE); - } else { - this.spillSlotValues.put(location, UnknownAllocationState.INSTANCE); - } - } - } - case RAVInstruction.Spill spill -> - this.spillSlotValues.putClone(spill.to, this.registerValues.get(ValueUtil.asRegister(spill.from))); - case RAVInstruction.Reload reload -> - this.registerValues.putClone(ValueUtil.asRegister(reload.to), this.spillSlotValues.get(reload.from)); - case RAVInstruction.Move move -> { - var value = this.registerValues.get(ValueUtil.asRegister(move.from)); - if (value.isUnknown()) { - // Hotfix for blockDefinitions, where if we moved a Value we need to make - // sure it's saved in the state, so that value is not propagated further - // causing Circular Exception - // TestCase: ConditionalElimination17 - value = new ValueAllocationState(Value.ILLEGAL); - } - - this.registerValues.putClone(ValueUtil.asRegister(move.to), value); - } - case RAVInstruction.StackMove move -> - this.spillSlotValues.putClone(move.to, this.spillSlotValues.get(move.from)); - case RAVInstruction.VirtualMove virtMove -> { - if (virtMove.location instanceof RegisterValue) { - this.registerValues.put(ValueUtil.asRegister(virtMove.location), new ValueAllocationState(virtMove.variableOrConstant)); - } else if (LIRValueUtil.isVariable(virtMove.location)) { - // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD - // Move before allocation - // TODO: maybe handle this better than VirtualMove with location as Variable - // TestCase: BoxingTest.boxBoolean - var locations = this.registerValues.getValueLocations(virtMove.variableOrConstant); - for (var location : locations) { - this.registerValues.put(location, new ValueAllocationState(virtMove.location)); - } - } else { - this.spillSlotValues.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); - } - } - default -> throw new IllegalStateException(); - } - } - } - - public abstract static class AllocationState { - public static AllocationState getDefault() { - return UnknownAllocationState.INSTANCE; - } - - public boolean isUnknown() { - return false; - } - - public boolean isConflicted() { - return false; - } - - public abstract AllocationState clone(); - - public abstract AllocationState meet(AllocationState other); - - public abstract boolean equals(AllocationState other); - } - - public static class ConflictedAllocationState extends AllocationState { - protected Set conflictedValues; - - public ConflictedAllocationState(Value value1, Value value2) { - this.conflictedValues = new HashSet<>(); - this.conflictedValues.add(value1); - this.conflictedValues.add(value2); - } - - private ConflictedAllocationState(Set conflictedValues) { - this.conflictedValues = new HashSet<>(conflictedValues); - } - - public void addConflictedValue(Value value) { - this.conflictedValues.add(value); - } - - public Set getConflictedValues() { - return this.conflictedValues; - } - - @Override - public boolean isConflicted() { - return true; - } - - @Override - public AllocationState meet(AllocationState other) { - if (other instanceof ValueAllocationState valueState) { - this.addConflictedValue(valueState.getValue()); - } - - if (other instanceof ConflictedAllocationState conflictedState) { - this.conflictedValues.addAll(conflictedState.conflictedValues); - } - - return this; - } - - @Override - public AllocationState clone() { - return new ConflictedAllocationState(this.conflictedValues); - } - - @Override - public boolean equals(AllocationState other) { - return other.isConflicted(); // TODO: handle contents - } - - @Override - public String toString() { - return "Conflicted {" + Arrays.toString(this.conflictedValues.toArray()) + "}"; - } - } - - public static class UnknownAllocationState extends AllocationState { - public static UnknownAllocationState INSTANCE = new UnknownAllocationState(); - - @Override - public boolean isUnknown() { - return true; - } - - @Override - public AllocationState meet(AllocationState other) { - return other; - } - - @Override - public AllocationState clone() { - return INSTANCE; - } - - @Override - public boolean equals(AllocationState other) { - return other == INSTANCE; - } - - @Override - public String toString() { - return "Unknown"; - } - } - - public static class ValueAllocationState extends AllocationState implements Cloneable { - protected Value value; - - public ValueAllocationState(Value value) { - if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { - // StackSlot, RegisterValue is present in start block in label as predefined argument - // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS - // ConstantValue act as Variable - - // We use variables as symbols for register validation - // but real registers can also be used as that, in some cases. - // TODO: reconsider handling of StackSlots - this.value = value; - } else { - throw new IllegalStateException(); - } - } - - public ValueAllocationState(ValueAllocationState other) { - this.value = other.getValue(); - } - - public Value getValue() { - return value; - } - - public AllocationState meet(AllocationState other) { - if (other.isUnknown()) { - return this; - } - - if (other.isConflicted()) { - return other; - } - - var otherValueAllocState = (ValueAllocationState) other; - if (!this.value.equals(otherValueAllocState.getValue())) { - return new ConflictedAllocationState(this.value, otherValueAllocState.getValue()); - } - - return this; - } - - @Override - public boolean equals(AllocationState other) { - return other instanceof ValueAllocationState otherVal && this.value.equals(otherVal.getValue()); - } - - @Override - public ValueAllocationState clone() { - return new ValueAllocationState(this); - } - - @Override - public String toString() { - return "Value {" + this.value + "}"; - } - } - - public interface AllocationStateMap extends Map, Cloneable { - AllocationState get(Object key); - - AllocationState putClone(K key, AllocationState value); - - boolean mergeWith(AllocationStateMap other); - - Set getValueLocations(Value value); - - int getTime(K key); - } - - @SuppressWarnings("serial") - public static class HashAllocationStateMap extends HashMap implements AllocationStateMap { - protected Map locationTimings; - protected int time; - - public HashAllocationStateMap() { - super(); - - locationTimings = new HashMap<>(); - time = 0; - } - - public HashAllocationStateMap(HashAllocationStateMap other) { - super(other); - - locationTimings = new HashMap<>(other.locationTimings); - time = other.time + 1; - } - - @Override - public AllocationState put(K key, AllocationState value) { - locationTimings.put(key, time++); - return super.put(key, value); - } - - @Override - public AllocationState get(Object key) { - var value = super.get(key); - if (value == null) { - return AllocationState.getDefault(); - } - - return value; - } - - public Set getValueLocations(Value value) { - Set locations = new HashSet<>(); - for (Map.Entry entry : entrySet()) { - if (entry.getValue() instanceof ValueAllocationState valState) { - if (valState.getValue().equals(value)) { - locations.add(entry.getKey()); - } - } - } - return locations; - } - - public int getTime(K key) { - return locationTimings.get(key); - } - - public AllocationState putClone(K key, AllocationState value) { - if (value.isConflicted() || value.isUnknown()) { - return this.put(key, value); - } - - return this.put(key, value.clone()); - } - - @Override - public boolean mergeWith(AllocationStateMap source) { - boolean changed = false; - for (var entry : source.entrySet()) { - if (!this.containsKey(entry.getKey())) { - changed = true; - - this.put(entry.getKey(), UnknownAllocationState.INSTANCE); - } - - var currentValue = this.get(entry.getKey()); - var result = this.get(entry.getKey()).meet(entry.getValue()); - if (!currentValue.equals(result)) { - changed = true; - } - - this.put(entry.getKey(), result); - } - - return changed; - } - } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java new file mode 100644 index 000000000000..590ad9900793 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -0,0 +1,30 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +public class UnknownAllocationState extends AllocationState { + public static UnknownAllocationState INSTANCE = new UnknownAllocationState(); + + @Override + public boolean isUnknown() { + return true; + } + + @Override + public AllocationState meet(AllocationState other) { + return other; + } + + @Override + public AllocationState clone() { + return INSTANCE; + } + + @Override + public boolean equals(AllocationState other) { + return other == INSTANCE; + } + + @Override + public String toString() { + return "Unknown"; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java new file mode 100644 index 000000000000..221494f401ef --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -0,0 +1,67 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.meta.Value; + +public class ValueAllocationState extends AllocationState implements Cloneable { + protected Value value; + + public ValueAllocationState(Value value) { + if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { + // StackSlot, RegisterValue is present in start block in label as predefined argument + // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS + // ConstantValue act as Variable + + // We use variables as symbols for register validation + // but real registers can also be used as that, in some cases. + // TODO: reconsider handling of StackSlots + this.value = value; + } else { + throw new IllegalStateException(); + } + } + + public ValueAllocationState(ValueAllocationState other) { + this.value = other.getValue(); + } + + public Value getValue() { + return value; + } + + public AllocationState meet(AllocationState other) { + if (other.isUnknown()) { + return this; + } + + if (other.isConflicted()) { + return other; + } + + var otherValueAllocState = (ValueAllocationState) other; + if (!this.value.equals(otherValueAllocState.getValue())) { + return new ConflictedAllocationState(this.value, otherValueAllocState.getValue()); + } + + return this; + } + + @Override + public boolean equals(AllocationState other) { + return other instanceof ValueAllocationState otherVal && this.value.equals(otherVal.getValue()); + } + + @Override + public ValueAllocationState clone() { + return new ValueAllocationState(this); + } + + @Override + public String toString() { + return "Value {" + this.value + "}"; + } +} From 872b3218b51ba4f7570b8e3e53709a83395146e4 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 6 Jan 2026 20:15:09 +0100 Subject: [PATCH 014/112] Tag constants instead of moving them in FromJump resolver --- .../verifier/MergedAllocationStateMap.java | 3 +- .../verifier/PreRegisterAllocationPhase.java | 340 ++++++++++++++++++ .../verifier/RegisterAllocationVerifier.java | 37 +- .../RegisterAllocationVerifierPhase.java | 307 +--------------- .../alloc/verifier/TaggedConstantFactory.java | 33 ++ .../verifier/TaggedDataPointerConstant.java | 54 +++ .../alloc/verifier/TaggedJavaConstant.java | 87 +++++ 7 files changed, 537 insertions(+), 324 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index e9ef78ae6d6a..a58fa7e058df 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -80,8 +80,7 @@ public MergedAllocationStateMap(MergedAllocationStateMap other) { } public AllocationState get(Value key) { - String keyString = this.getValueKeyString(key); - return internalMap.get(keyString); + return this.get(key, AllocationState.getDefault()); } public AllocationState get(Value key, AllocationState defaultValue) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java new file mode 100644 index 000000000000..863771670db1 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -0,0 +1,340 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.InstructionStateProcedure; +import jdk.graal.compiler.lir.InstructionValueProcedure; +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.StandardOp; +import jdk.graal.compiler.lir.ValueProcedure; +import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.graal.compiler.lir.gen.LIRGenerationResult; +import jdk.graal.compiler.lir.gen.LIRGenerator; +import jdk.graal.compiler.lir.phases.AllocationPhase; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.Value; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class PreRegisterAllocationPhase extends AllocationPhase { + protected Map preallocMap; + protected PhiResolution phiResolution; + protected TaggedConstantFactory taggedConstantFactory; + protected boolean moveConstants; + + protected PreRegisterAllocationPhase(PhiResolution phiResolution, boolean moveConstants) { + this.preallocMap = new HashMap<>(); + this.phiResolution = phiResolution; + this.taggedConstantFactory = new TaggedConstantFactory(); + this.moveConstants = moveConstants; + } + + public static class ConstantOverrideValueProcedure implements ValueProcedure { + private LIR lir; + private List variables; + private Map constantValueMap; + private Method nextVariableMethod; + + public ConstantOverrideValueProcedure(LIR lir, Map constantValueMap) { + this.lir = lir; + this.constantValueMap = constantValueMap; + + try { + this.nextVariableMethod = LIRGenerator.VariableProvider.class.getDeclaredMethod("nextVariable"); + this.nextVariableMethod.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public void setVariableList(List variables) { + this.variables = variables; + } + + public int nextVariable() { + try { + return (int) nextVariableMethod.invoke((LIRGenerator.VariableProvider) this.lir); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (value instanceof ConstantValue constant) { + var variable = new Variable(constant.getValueKind(), nextVariable()); + constantValueMap.put(variable, constant); + variables.add(variable); + return variable; + } + + return value; + } + } + + protected InstructionValueProcedure taggedConstantValueProc = new InstructionValueProcedure() { + @Override + public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (value instanceof ConstantValue constantValue) { + return new ConstantValue(constantValue.getValueKind(), taggedConstantFactory.createNew(constantValue.getConstant())); + } + + return value; + } + }; + + public Map getPreallocMap() { + return preallocMap; + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + var compUnitName = lirGenRes.getCompilationUnitName(); + if (RegisterAllocationVerifierPhase.isIgnored(compUnitName)) { + return; + } + + LIR lir = lirGenRes.getLIR(); + + Map constantValueMap = new HashMap<>(); + var constOverwriteProc = new ConstantOverrideValueProcedure(lir, constantValueMap); + + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + ArrayList instructions = lir.getLIRforBlock(block); + + List newVars = new LinkedList<>(); + constOverwriteProc.setVariableList(newVars); + + RAVInstruction.Base previousInstr = null; + for (var instruction : instructions) { + if (instruction instanceof StandardOp.JumpOp && this.phiResolution == PhiResolution.FromJump) { + if (this.moveConstants) { + instruction.forEachAlive(constOverwriteProc); + } else { + instruction.forEachAlive(taggedConstantValueProc); + } + } + + if (this.isVirtualMove(instruction)) { + // Virtual moves (variable = MOV real register) are going to be removed by the allocator, + // but we still need the information about which variables are associated to which real + // registers, and so we store them. They are generally associated to other instructions + // that's why we append them here to the previous instruction (for example Label or Foreign Call) + // use these, if this instruction was deleted in the allocator, then they will be missing too. + assert previousInstr != null; + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var location = valueMov.getInput(); + var variable = LIRValueUtil.asVariable(valueMov.getResult()); + + var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, location); + previousInstr.addVirtualMove(virtualMove); + continue; // No need to store virtual move here, it is stored into previous instruction. + } + + boolean speculative = false; + if (this.isSpeculativeMove(instruction)) { + speculative = true; + // Speculative moves are in form ry = MOVE vx, which could be removed if variable + // ends up being allocated to the same register as ry. If it was removed + // we need to re-add it because it holds important information about where value of + // this variable is placed - for label resolution after the label. + assert previousInstr != null; + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var variable = LIRValueUtil.asVariable(valueMov.getInput()); + var register = valueMov.getResult(); + + var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); + previousInstr.addSpeculativeMove(virtualMove); + } + + var opRAVInstr = new RAVInstruction.Op(instruction); + + instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); + instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); + instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); + instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); + + this.preallocMap.put(instruction, opRAVInstr); + + if (!speculative) { + previousInstr = opRAVInstr; + } + } + + if (newVars.isEmpty()) { + continue; + } + + var j = instructions.removeLast(); + var it = newVars.iterator(); + while (it.hasNext()) { + var v = LIRValueUtil.asVariable(it.next()); + var mov = context.spillMoveFactory.createMove(v, constantValueMap.get(v)); + var ravInstr = new RAVInstruction.Op(mov); + mov.forEachOutput(ravInstr.dests.copyOriginalProc); + this.preallocMap.put(mov, ravInstr); + instructions.add(mov); + } + + instructions.add(j); + } + } + + public BlockMap> getVerifierInstructions(LIR lir) { + Set presentInstructions = new HashSet<>(); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + ArrayList instructions = lir.getLIRforBlock(block); + presentInstructions.addAll(instructions); + } + + BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + var instructionList = new LinkedList(); + + ArrayList instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + var rAVInstr = preallocMap.get(instruction); + if (rAVInstr == null) { + var movOp = this.getRAVMoveInstruction(instruction); + if (movOp != null) { + instructionList.add(movOp); + continue; + } + + throw new VerErr.UnknownInstructionError(instruction, block); + } + + var opRAVInstr = (RAVInstruction.Op) rAVInstr; + + instruction.forEachInput(opRAVInstr.uses.copyCurrentProc); + instruction.forEachOutput(opRAVInstr.dests.copyCurrentProc); + instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); + instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); + instruction.forEachState(new InstructionStateProcedure() { + @Override + public void doState(LIRInstruction instruction, LIRFrameState state) { + + } + }); + + instructionList.add(opRAVInstr); + var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); + + if (!speculativeMoves.isEmpty()) { + for (var speculativeMove : speculativeMoves) { + if (!presentInstructions.contains(speculativeMove.getLIRInstruction())) { + instructionList.add(speculativeMove); + } + } + } + + var virtualMoves = opRAVInstr.getVirtualMoveList(); + instructionList.addAll(virtualMoves); + } + + blockInstructions.put(block, instructionList); + } + return blockInstructions; + } + + /** + * Create Register Verifier Instruction that was created by the Register Allocator. + * Generally speaking, it's always a move instruction, other ones return null. + * + * @param instruction LIRInstruction newly created by Register Allocator + * @return Spill, Reload, Move or null if instruction is not a move + */ + protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + if (instruction.isLoadConstantOp()) { + var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); + var constant = constatLoad.getConstant(); + var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot + + // This isn't really a virtual move, but it currently acts the same, so we keep it, + // we take constants as variables. TODO: maybe remove virtual move altogether for Move(reg, var/constant) + return new RAVInstruction.VirtualMove(instruction, new ConstantValue(result.getValueKind(), constant), result); + } + + return null; + } + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + + var input = valueMov.getInput(); + var result = valueMov.getResult(); + + if (input instanceof VirtualStackSlot stackSlot && result instanceof RegisterValue reg) { + return new RAVInstruction.Reload(instruction, reg, stackSlot); + } else if (result instanceof VirtualStackSlot stackSlot && input instanceof RegisterValue reg) { + return new RAVInstruction.Spill(instruction, stackSlot, reg); + } else if (input instanceof RegisterValue reg1 && result instanceof RegisterValue reg2) { + return new RAVInstruction.Move(instruction, reg1, reg2); + } else if (input instanceof StackSlot stackSlot && result instanceof RegisterValue reg) { + return new RAVInstruction.Reload(instruction, reg, stackSlot); + } else if (input instanceof RegisterValue reg && result instanceof StackSlot stackSlot) { + return new RAVInstruction.Spill(instruction, stackSlot, reg); + } + + if (input instanceof StackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof StackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } + + return null; + } + + /** + * Determines if instruction is a virtual move, a virtual move is + * a move instruction that moves a real register value into a variable, + * which is something that will always get removed from the final allocated + * IR. + * + * @param instruction LIRInstruction we are looking at + * @return true, if instruction is a virtual move, otherwise false + */ + protected boolean isVirtualMove(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return false; + } + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var input = valueMov.getInput(); + return (input instanceof RegisterValue || input instanceof StackSlot /*|| input instanceof AbstractAddress*/) && LIRValueUtil.isVariable(valueMov.getResult()); + } + + protected boolean isSpeculativeMove(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return false; + } + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var result = valueMov.getResult(); // Result could be variable or register + return (result instanceof RegisterValue || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index dda5ef07af4d..2557c9a98360 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -33,8 +33,7 @@ public final class RegisterAllocationVerifier { public Map usageAliasMap; // FromPredecessors resolver - public BlockMap blockDefinitions; - public BlockMap> blockDefinitions2; + public BlockMap> blockDefinitions; public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution) { this.lir = lir; @@ -362,7 +361,6 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O continue; } - // TODO: maybe we just need the locations to have size 1 to stop locations.retainAll(varLoc); } @@ -419,18 +417,18 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var propagateMap = new HashMap, List>(); var variableToRegisters = new HashMap(); var defBlockVariablesToPropagate = new LinkedList(); - var defForEntry = this.blockDefinitions.get(defBlock); + // var defForEntry = this.blockDefinitions.get(defBlock); for (int i = 0; i < labelInstr.dests.count; i++) { var register = (RegisterValue) labelInstr.dests.curr[i]; var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - var registerDefinition = defForEntry.values.get(register); - if (registerDefinition.isUnknown()) { - defForEntry.values.put(register, new ValueAllocationState(variable)); - } + // var registerDefinition = defForEntry.values.get(register); + // if (registerDefinition.isUnknown()) { + // defForEntry.values.put(register, new ValueAllocationState(variable)); + // } defBlockVariablesToPropagate.add(variable); - variableToRegisters.put(variable, register); + variableToRegisters.put(variable, register); } Queue> worklist = new LinkedList<>(); @@ -462,14 +460,7 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var variable = LIRValueUtil.asVariable(itToPropagate.next()); var register = variableToRegisters.get(variable); - var registerDefinition = def.values.get(register); - if (!registerDefinition.isUnknown() && registerDefinition instanceof ValueAllocationState valState && !valState.getValue().equals(variable)) { - // This block has redefined the value of said register, - // and we will not pass it further. - continue; - } - - if (this.blockDefinitions2.get(curr).contains(register)) { + if (def.contains(register)) { continue; } @@ -764,17 +755,7 @@ public boolean run() { // Currently only useful to this resolution type, but later // we should only go through instructions once and then just merge // with entry state and successors. - for (var blockId : this.lir.getBlocks()) { - var block = this.lir.getBlockById(blockId); - var instructions = this.blockInstructions.get(block); - var state = new MergedBlockVerifierState(phiResolution); - for (var instr : instructions) { - state.update(instr); - } - this.blockDefinitions.put(block, state); - } - - this.blockDefinitions2 = this.getDefinitionSets(); + this.blockDefinitions = this.getDefinitionSets(); } this.calculateEntryBlocks(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 94138f81061e..1de81ea27c31 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -43,8 +43,8 @@ public static class Options { @Option(help = "Select which way you want to resolve phi arguments.", type = OptionType.Debug) public static final EnumOptionKey RAPhiResolution = new EnumOptionKey<>(PhiResolution.FromUsage); - // @Option(help = "Should constants be moved to variables", type = OptionType.Debug) - // public static final OptionKey MoveConstants = new OptionKey<>(false); + @Option(help = "Should constants be moved to variables when needed", type = OptionType.Debug) + public static final OptionKey MoveConstants = new OptionKey<>(true); } public static String[] ignoredTestCases = { @@ -53,6 +53,7 @@ public static class Options { "truffle", "Truffle", "polyglot", + "Polyglot", "Root[]", "InstrumentationTestLanguage", "NFITest", @@ -70,21 +71,27 @@ public static boolean isIgnored(String compUnitName) { } private PhiResolution phiResolution; + private boolean moveConstants; public RegisterAllocationVerifierPhase(OptionValues options) { this.phiResolution = Options.RAPhiResolution.getValue(options); + this.moveConstants = Options.MoveConstants.getValue(options); } - protected RAVerifierPreAllocPhase preallocPhaseRAVerifier; + protected PreRegisterAllocationPhase preallocPhaseRAVerifier; + // protected RAVerifierPreAllocPhase preallocPhaseRAVerifier; public AllocationPhase getPreAllocPhase() { - this.preallocPhaseRAVerifier = new RAVerifierPreAllocPhase(phiResolution); + this.preallocPhaseRAVerifier = new PreRegisterAllocationPhase(phiResolution, moveConstants); return this.preallocPhaseRAVerifier; } @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { var compUnitName = lirGenRes.getCompilationUnitName(); + // if (!compUnitName.contains("nestedSum")) { + // return; + // } if (RegisterAllocationVerifierPhase.isIgnored(compUnitName)) { // Skipping truffle test cases because they cause trouble with @@ -95,7 +102,8 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo assert this.preallocPhaseRAVerifier != null : "Phase before register allocation was not run, cannot verify it."; - var instructions = this.getVerifierInstructions(lirGenRes.getLIR()); + // var instructions = this.getVerifierInstructions(lirGenRes.getLIR()); + var instructions = this.preallocPhaseRAVerifier.getVerifierInstructions(lirGenRes.getLIR()); var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.phiResolution); if (!verifier.run()) { // TODO: every error generated by the verifier needs to be a lot more verbose. @@ -105,293 +113,4 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo System.err.println("Verification failed - " + compUnitName); } } - - /** - * Create Register Verifier Instruction that was created by the Register Allocator. - * Generally speaking, it's always a move instruction, other ones return null. - * - * @param instruction LIRInstruction newly created by Register Allocator - * @return Spill, Reload, Move or null if instruction is not a move - */ - protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - if (instruction.isLoadConstantOp()) { - var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); - var constant = constatLoad.getConstant(); - var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot - - // This isn't really a virtual move, but it currently acts the same, so we keep it, - // we take constants as variables. TODO: maybe remove virtual move altogether for Move(reg, var/constant) - return new RAVInstruction.VirtualMove(instruction, new ConstantValue(result.getValueKind(), constant), result); - } - - return null; - } - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - - var input = valueMov.getInput(); - var result = valueMov.getResult(); - - if (input instanceof VirtualStackSlot stackSlot && result instanceof RegisterValue reg) { - return new RAVInstruction.Reload(instruction, reg, stackSlot); - } else if (result instanceof VirtualStackSlot stackSlot && input instanceof RegisterValue reg) { - return new RAVInstruction.Spill(instruction, stackSlot, reg); - } else if (input instanceof RegisterValue reg1 && result instanceof RegisterValue reg2) { - return new RAVInstruction.Move(instruction, reg1, reg2); - } else if (input instanceof StackSlot stackSlot && result instanceof RegisterValue reg) { - return new RAVInstruction.Reload(instruction, reg, stackSlot); - } else if (input instanceof RegisterValue reg && result instanceof StackSlot stackSlot) { - return new RAVInstruction.Spill(instruction, stackSlot, reg); - } - - if (input instanceof StackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof StackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } - - return null; - } - - protected BlockMap> getVerifierInstructions(LIR lir) { - Set presentInstructions = new HashSet<>(); - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - ArrayList instructions = lir.getLIRforBlock(block); - presentInstructions.addAll(instructions); - } - - BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); - var preallocMap = preallocPhaseRAVerifier.getPreallocMap(); - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - var instructionList = new LinkedList(); - - ArrayList instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - var rAVInstr = preallocMap.get(instruction); - if (rAVInstr == null) { - var movOp = this.getRAVMoveInstruction(instruction); - if (movOp != null) { - instructionList.add(movOp); - continue; - } - - throw new GraalError("Unknown instruction type for verification - " + instruction); - } - - var opRAVInstr = (RAVInstruction.Op) rAVInstr; - - instruction.forEachInput(opRAVInstr.uses.copyCurrentProc); - instruction.forEachOutput(opRAVInstr.dests.copyCurrentProc); - instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); - instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); - - instructionList.add(opRAVInstr); - var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); - - if (!speculativeMoves.isEmpty()) { - for (var speculativeMove : speculativeMoves) { - if (!presentInstructions.contains(speculativeMove.getLIRInstruction())) { - instructionList.add(speculativeMove); - } - } - } - - var virtualMoves = opRAVInstr.getVirtualMoveList(); - instructionList.addAll(virtualMoves); - } - - blockInstructions.put(block, instructionList); - } - return blockInstructions; - } - - public static class ConstantOverrideValueProcedure implements ValueProcedure { - private LIR lir; - private List variables; - private Map constantValueMap; - private Method nextVariableMethod; - - public ConstantOverrideValueProcedure(LIR lir, Map constantValueMap) { - this.lir = lir; - this.constantValueMap = constantValueMap; - - try { - this.nextVariableMethod = LIRGenerator.VariableProvider.class.getDeclaredMethod("nextVariable"); - this.nextVariableMethod.setAccessible(true); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - public void setVariableList(List variables) { - this.variables = variables; - } - - public int nextVariable() { - try { - return (int) nextVariableMethod.invoke((LIRGenerator.VariableProvider) this.lir); - } catch (InvocationTargetException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (value instanceof ConstantValue constant) { - var variable = new Variable(constant.getValueKind(), nextVariable()); - constantValueMap.put(variable, constant); - variables.add(variable); - return variable; - } - - return value; - } - } - - public static class RAVerifierPreAllocPhase extends AllocationPhase { - protected Map preallocMap; - protected PhiResolution phiResolution; - - protected RAVerifierPreAllocPhase(PhiResolution phiResolution) { - this.preallocMap = new HashMap<>(); - this.phiResolution = phiResolution; - } - - public Map getPreallocMap() { - return preallocMap; - } - - @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - var compUnitName = lirGenRes.getCompilationUnitName(); - if (RegisterAllocationVerifierPhase.isIgnored(compUnitName)) { - return; - } - - Map constantValueMap = new HashMap<>(); - - LIR lir = lirGenRes.getLIR(); - var constOverwriteProc = new ConstantOverrideValueProcedure(lir, constantValueMap); - - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - ArrayList instructions = lir.getLIRforBlock(block); - - List newVars = new LinkedList<>(); - constOverwriteProc.setVariableList(newVars); - - RAVInstruction.Base previousInstr = null; - for (var instruction : instructions) { - if ( - // @formatter:off - instruction instanceof StandardOp.JumpOp - && this.phiResolution == PhiResolution.FromJump - // @formatter:on - ) { - instruction.forEachAlive(constOverwriteProc); - } - - if (this.isVirtualMove(instruction)) { - // Virtual moves (variable = MOV real register) are going to be removed by the allocator, - // but we still need the information about which variables are associated to which real - // registers, and so we store them. They are generally associated to other instructions - // that's why we append them here to the previous instruction (for example Label or Foreign Call) - // use these, if this instruction was deleted in the allocator, then they will be missing too. - assert previousInstr != null; - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var location = valueMov.getInput(); - var variable = LIRValueUtil.asVariable(valueMov.getResult()); - - var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, location); - previousInstr.addVirtualMove(virtualMove); - - continue; // No need to store virtual move here, it is stored into previous instruction. - } - - boolean speculative = false; - if (this.isSpeculativeMove(instruction)) { - speculative = true; - // Speculative moves are in form ry = MOVE vx, which could be removed if variable - // ends up being allocated to the same register as ry. If it was removed - // we need to re-add it because it holds important information about where value of - // this variable is placed - for label resolution after the label. - assert previousInstr != null; - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var variable = LIRValueUtil.asVariable(valueMov.getInput()); - var register = valueMov.getResult(); - - var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); - previousInstr.addSpeculativeMove(virtualMove); - } - - var opRAVInstr = new RAVInstruction.Op(instruction); - - instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); - instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); - instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); - instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); - - this.preallocMap.put(instruction, opRAVInstr); - - if (!speculative) { - previousInstr = opRAVInstr; - } - } - - if (newVars.isEmpty()) { - continue; - } - - var j = instructions.removeLast(); - var it = newVars.iterator(); - while (it.hasNext()) { - var v = LIRValueUtil.asVariable(it.next()); - var mov = context.spillMoveFactory.createMove(v, constantValueMap.get(v)); - var ravInstr = new RAVInstruction.Op(mov); - mov.forEachOutput(ravInstr.dests.copyOriginalProc); - this.preallocMap.put(mov, ravInstr); - instructions.add(mov); - } - - instructions.add(j); - } - } - - /** - * Determines if instruction is a virtual move, a virtual move is - * a move instruction that moves a real register value into a variable, - * which is something that will always get removed from the final allocated - * IR. - * - * @param instruction LIRInstruction we are looking at - * @return true, if instruction is a virtual move, otherwise false - */ - protected boolean isVirtualMove(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - return false; - } - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var input = valueMov.getInput(); - return (input instanceof RegisterValue || input instanceof StackSlot /*|| input instanceof AbstractAddress*/) && LIRValueUtil.isVariable(valueMov.getResult()); - } - - protected boolean isSpeculativeMove(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - return false; - } - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var result = valueMov.getResult(); // Result could be variable or register - return (result instanceof RegisterValue || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); - } - } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java new file mode 100644 index 000000000000..d75ef6828ce4 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java @@ -0,0 +1,33 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.type.DataPointerConstant; +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaConstant; + +public class TaggedConstantFactory { + protected int lastTag; + + public TaggedConstantFactory(int initialTag) { + this.lastTag = initialTag; + } + + public TaggedConstantFactory() { + this(0); + } + + public Constant createNew(Constant constant) { + return switch (constant) { + case JavaConstant javaConst -> this.createNew(javaConst); + case DataPointerConstant ptrConst -> this.createNew(ptrConst); + default -> throw new IllegalStateException(); + }; + } + + public Constant createNew(JavaConstant constant) { + return new TaggedJavaConstant(constant, this.lastTag++); + } + + public Constant createNew(DataPointerConstant constant) { + return new TaggedDataPointerConstant(constant, this.lastTag++); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java new file mode 100644 index 000000000000..329d83380831 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java @@ -0,0 +1,54 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.type.DataPointerConstant; + +import java.nio.ByteBuffer; + +public class TaggedDataPointerConstant extends DataPointerConstant { + protected DataPointerConstant constant; + protected int tag; + + protected TaggedDataPointerConstant(DataPointerConstant constant, int tag) { + super(constant.getAlignment()); + this.constant = constant; + this.tag = tag; + } + + @Override + public int getSerializedSize() { + return this.constant.getSerializedSize(); + } + + @Override + public void serialize(ByteBuffer buffer) { + this.constant.serialize(buffer); + } + + @Override + public String toValueString() { + return this.constant.toValueString(); + } + + @Override + public int hashCode() { + return this.tag ^ constant.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof TaggedDataPointerConstant otherTagged) { + return otherTagged.tag == tag && this.constant.equals(otherTagged.constant); + } + + return constant.equals(o); + } + + @Override + public String toString() { + return "t" + this.tag + ":" + constant.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java new file mode 100644 index 000000000000..0c7fad8fb336 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java @@ -0,0 +1,87 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; + +public class TaggedJavaConstant implements JavaConstant { + protected JavaConstant constant; + protected int tag; + + protected TaggedJavaConstant(JavaConstant constant, int tag) { + this.constant = constant; + this.tag = tag; + } + + @Override + public int hashCode() { + return this.tag ^ constant.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof TaggedJavaConstant otherTagged) { + return otherTagged.tag == tag && this.constant.equals(otherTagged.constant); + } + + return constant.equals(o); + } + + @Override + public String toString() { + return "t" + this.tag + ":" + constant.toString(); + } + + @Override + public JavaKind getJavaKind() { + return this.constant.getJavaKind(); + } + + @Override + public boolean isNull() { + return this.constant.isNull(); + } + + @Override + public boolean isDefaultForKind() { + return this.constant.isDefaultForKind(); + } + + @Override + public Object asBoxedPrimitive() { + return this.constant.asBoxedPrimitive(); + } + + @Override + public int asInt() { + return this.constant.asInt(); + } + + @Override + public boolean asBoolean() { + return this.constant.asBoolean(); + } + + @Override + public long asLong() { + return this.constant.asLong(); + } + + @Override + public float asFloat() { + return this.constant.asFloat(); + } + + @Override + public double asDouble() { + return this.constant.asDouble(); + } + + @Override + public String toValueString() { + return this.constant.toValueString(); + } +} From b1972440b7f7b5c54794ec77386387ee8b2ba2c4 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 7 Jan 2026 00:20:43 +0100 Subject: [PATCH 015/112] Fix FromUsage resolver exploding in memory usage --- .../lir/alloc/verifier/DefinitionSet.java | 42 +++ .../lir/alloc/verifier/FromUsageResolver.java | 290 ++++++++++++++++++ .../verifier/RegisterAllocationVerifier.java | 256 +--------------- 3 files changed, 342 insertions(+), 246 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java new file mode 100644 index 000000000000..5eff73cff012 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java @@ -0,0 +1,42 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class DefinitionSet { + protected Set internalSet; + protected Map valueMap; + + public DefinitionSet() { + this.internalSet = new HashSet<>(); + this.valueMap = new HashMap<>(); + } + + public void add(Value value) { + if (Value.ILLEGAL.equals(value)) { + return; + } + + String valueString = this.getValueKeyString(value); + this.valueMap.put(valueString, value); + this.internalSet.add(valueString); + } + + public boolean contains(Value value) { + String valueString = this.getValueKeyString(value); + return this.internalSet.contains(valueString); + } + + protected String getValueKeyString(Value value) { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + return value.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java new file mode 100644 index 000000000000..7b266292a18d --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -0,0 +1,290 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +class FromUsageResolver { + protected LIR lir; + protected BlockMap> blockInstructions; + + public Map labelMap; + public Map variableRegisterMap; + public Map usageAliasMap; + + class PathEntry { + public BasicBlock block; + public PathEntry previous; + + public PathEntry(BasicBlock block) { + this.block = block; + this.previous = null; + } + + public PathEntry(BasicBlock block, PathEntry previous) { + this.block = block; + this.previous = previous; + } + + public PathEntry next(BasicBlock block) { + return new PathEntry(block, this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o instanceof PathEntry le && le.block.equals(this.block)) { + if (le.previous == null) return this.previous == null; + return le.previous.block.equals(this.previous.block); + } + + return false; + } + + @Override + public int hashCode() { + return this.previous == null ? block.hashCode() : block.hashCode() ^ previous.block.hashCode(); + } + + @Override + public String toString() { + return (previous == null ? "" : previous.toString()) + " -> " + block.toString(); + } + } + + protected FromUsageResolver(LIR lir, BlockMap> blockInstructions) { + this.lir = lir; + this.blockInstructions = blockInstructions; + + this.labelMap = new HashMap<>(); + this.variableRegisterMap = new HashMap<>(); + this.usageAliasMap = new HashMap<>(); + } + + private Value getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVInstruction.Base usageInstruction, Value location, Variable variable) { + boolean reachedUsage = false; + while (true) { + assert path != null; + + var block = path.block; + var instructions = this.blockInstructions.get(block).reversed(); + for (var instruction : instructions) { + if (instruction == usageInstruction) { + reachedUsage = true; + continue; + } + + if (!reachedUsage) { + continue; + } + + // Tracking the value bottom up, from the usage up to the label definition + // looking for any changes to the target register that could highlight + // different register is supposed to be used, in case of reload/spill combo or + // register move. If we are wrong about the target register then, it will + // get thrown out in the verification stage. TODO: maybe try to use multiple usages to be sure? + switch (instruction) { + case RAVInstruction.Spill spill -> { + if (spill.to.equals(location)) { + location = spill.from; + } + } + case RAVInstruction.Move move -> { + if (move.to.equals(location)) { + location = move.from; + } + } + case RAVInstruction.Reload reload -> { + if (reload.to.equals(location)) { + location = reload.from; + } + } + case RAVInstruction.VirtualMove move -> { + if (move.location.equals(location) && !move.variableOrConstant.equals(variable)) { + // TODO: change this exception + throw GraalError.shouldNotReachHere("Target register is overwritten without backup."); + } + } + // For Op, if there is a redefinition, we let the later stages handle that + default -> { + } + } + } + + if (path.block.equals(defBlock)) { + break; + } + + path = path.previous; + } + + return location; + } + + private void mapLabelVariableFromUsage( + // @formatter:off + Map> labelToBlockMap, + Variable variable, Value location, + PathEntry path, RAVInstruction.Base useInstruction) + // @formatter:on + { + var variableLabelInstr = this.labelMap.get(variable); + if (variableLabelInstr == null) { + return; + } + + var labelBlock = labelToBlockMap.get(variableLabelInstr); + var register = this.getLocationFromUsage(path, labelBlock, useInstruction, location, variable); + + this.variableRegisterMap.put(variable, register); + this.labelMap.remove(variable); + this.usageAliasMap.remove(variable); + + for (int j = 0; j < variableLabelInstr.dests.count; j++) { + if (variableLabelInstr.dests.orig[j].equals(variable)) { + variableLabelInstr.dests.curr[j] = register; + + // Need to iterate over predecessors and fill jumps as well + for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { + var pred = labelBlock.getPredecessorAt(k); + var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); + jumpOp.alive.curr[j] = register; + } + } + } + + for (var aliasEntry : this.usageAliasMap.entrySet()) { + var originalVariable = LIRValueUtil.asVariable(aliasEntry.getValue()); + if (!originalVariable.equals(variable)) { + continue; + } + + var aliasVariable = LIRValueUtil.asVariable(aliasEntry.getKey()); + var aliasLabelInstr = this.labelMap.get(aliasVariable); + if (aliasLabelInstr == null) { + continue; + } + + this.labelMap.remove(aliasVariable); + + var aliasLabelBlock = labelToBlockMap.get(aliasLabelInstr); + for (int j = 0; j < aliasLabelInstr.dests.count; j++) { + if (aliasLabelInstr.dests.orig[j].equals(aliasVariable)) { + aliasLabelInstr.dests.curr[j] = register; + + // Need to iterate over predecessors and fill jumps as well + for (int k = 0; k < aliasLabelBlock.getPredecessorCount(); k++) { + var pred = aliasLabelBlock.getPredecessorAt(k); + var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); + jumpOp.alive.curr[j] = register; + } + } + } + } + } + + private void tryMappingVariable( + // @formatter:off + Map> labelToBlockMap, Value variable, + Value location, PathEntry path, RAVInstruction.Base useInstruction + // @formatter:on + ) { + if (!LIRValueUtil.isVariable(variable)) { + return; + } + + if (location instanceof Variable || location instanceof ConstantValue) { + return; + } + + this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(variable), location, path, useInstruction); + } + + /** + * Resolves label variable registers by finding where they are used. + */ + public void resolvePhiFromUsage() { + Queue worklist = new LinkedList<>(); + worklist.add(new PathEntry(this.lir.getControlFlowGraph().getStartBlock())); + + Map> labelToBlockMap = new HashMap<>(); + + Set visited = new HashSet<>(); // Ignore already visited combinations. + while (!worklist.isEmpty()) { + var entry = worklist.poll(); + if (visited.contains(entry)) { + continue; + } + visited.add(entry); + + var block = entry.block; + var instructions = this.blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + for (int i = 0; i < labelInstr.dests.count; i++) { + if (labelInstr.dests.curr[i] == null) { + Variable variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + this.labelMap.put(variable, labelInstr); + labelToBlockMap.put(labelInstr, block); + } + } + + for (var instruction : instructions) { + switch (instruction) { + case RAVInstruction.VirtualMove move -> { + this.tryMappingVariable(labelToBlockMap, move.variableOrConstant, move.location, entry, instruction); + } + case RAVInstruction.Op op -> { + if (instruction.lirInstruction instanceof StandardOp.JumpOp) { + // Always only one successor for this jump op + // Assumption here is, that we resolve aliases with original registers immediately + // so in-case an alias was defined after that happened, it's not resolved and will fail. + var label = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); + for (int i = 0; i < op.alive.count; i++) { + if (!LIRValueUtil.isVariable(op.alive.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(op.alive.orig[i]); + if (this.labelMap.get(variable) != null) { + this.usageAliasMap.put(variable, LIRValueUtil.asVariable(label.dests.orig[i])); + } + } + + continue; + } + + for (int i = 0; i < op.uses.count; i++) { + this.tryMappingVariable(labelToBlockMap, op.uses.orig[i], op.uses.curr[i], entry, instruction); + } + + for (int i = 0; i < op.alive.count; i++) { + this.tryMappingVariable(labelToBlockMap, op.alive.orig[i], op.alive.curr[i], entry, instruction); + } + } + default -> { + } + } + } + + for (int i = 0; i < block.getSuccessorCount(); i++) { + var succ = block.getSuccessorAt(i); + worklist.add(entry.next(succ)); + } + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 2557c9a98360..3ab57305459e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -6,7 +6,6 @@ import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; @@ -33,7 +32,9 @@ public final class RegisterAllocationVerifier { public Map usageAliasMap; // FromPredecessors resolver - public BlockMap> blockDefinitions; + public BlockMap blockDefinitions; + + public FromUsageResolver fromUsageResolver; public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution) { this.lir = lir; @@ -50,6 +51,8 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.labelMap = new HashMap<>(); this.variableRegisterMap = new HashMap<>(); this.usageAliasMap = new HashMap<>(); + + this.fromUsageResolver = new FromUsageResolver(lir, blockInstructions); } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -62,241 +65,12 @@ private boolean doPrecessorsHaveStates(BasicBlock block) { return true; } - private RegisterValue getRegisterFromUsage( - LinkedList> path, - BasicBlock defBlock, - RAVInstruction.Base usageInstruction, - RegisterValue register, - Variable variable - ) { - while (!path.isEmpty()) { - var block = path.peek(); - if (block == defBlock) { - break; - } - - path.poll(); - } - - Value stackSlot = null; - boolean reachedUsage = false; - for (var block : path.reversed()) { // TODO: reconsider circular paths - var instructions = this.blockInstructions.get(block).reversed(); - for (var instruction : instructions) { - if (instruction == usageInstruction) { - reachedUsage = true; - continue; - } - - if (!reachedUsage) { - continue; - } - - // Tracking the value bottom up, from the usage up to the label definition - // looking for any changes to the target register that could highlight - // different register is supposed to be used, in case of reload/spill combo or - // register move. If we are wrong about the target register then, it will - // get thrown out in the verification stage. TODO: maybe try to use multiple usages to be sure? - switch (instruction) { - case RAVInstruction.Spill spill -> { - if (spill.to.equals(stackSlot)) { - register = spill.from; - } - } - case RAVInstruction.Move move -> { - if (move.to.equals(register)) { - register = move.from; - } - } - case RAVInstruction.Reload reload -> { - if (reload.to.equals(register)) { - register = null; // No longer holds the variable - stackSlot = reload.from; - } - } - case RAVInstruction.VirtualMove move -> { - if (move.location.equals(register) && !move.variableOrConstant.equals(variable)) { - throw new Error("Target register has different variable."); // TODO: deal with this when we find an example - } - } - // For Op, if there is a redefinition, we let the later stages handle that - default -> { - } - } - } - } - - return register; - } - - private void mapLabelVariableFromUsage( - // @formatter:off - Map> labelToBlockMap, - Variable variable, RegisterValue regGuess, - LinkedList> path, RAVInstruction.Base useInstruction) - // @formatter:on - { - var variableLabelInstr = this.labelMap.get(variable); - if (variableLabelInstr == null) { - return; - } - - var labelBlock = labelToBlockMap.get(variableLabelInstr); - var register = this.getRegisterFromUsage(path, labelBlock, useInstruction, regGuess, variable); - - this.variableRegisterMap.put(variable, register); - this.labelMap.remove(variable); - this.usageAliasMap.remove(variable); - - for (int j = 0; j < variableLabelInstr.dests.count; j++) { - if (variableLabelInstr.dests.orig[j].equals(variable)) { - variableLabelInstr.dests.curr[j] = register; - - // Need to iterate over predecessors and fill jumps as well - for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { - var pred = labelBlock.getPredecessorAt(k); - var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); - jumpOp.alive.curr[j] = register; - } - } - } - - for (var aliasEntry : this.usageAliasMap.entrySet()) { - var originalVariable = LIRValueUtil.asVariable(aliasEntry.getValue()); - if (!originalVariable.equals(variable)) { - continue; - } - - var aliasVariable = LIRValueUtil.asVariable(aliasEntry.getKey()); - var aliasLabelInstr = this.labelMap.get(aliasVariable); - if (aliasLabelInstr == null) { - continue; - } - - this.labelMap.remove(aliasVariable); - - var aliasLabelBlock = labelToBlockMap.get(aliasLabelInstr); - for (int j = 0; j < aliasLabelInstr.dests.count; j++) { - if (aliasLabelInstr.dests.orig[j].equals(aliasVariable)) { - aliasLabelInstr.dests.curr[j] = register; - - // Need to iterate over predecessors and fill jumps as well - for (int k = 0; k < aliasLabelBlock.getPredecessorCount(); k++) { - var pred = aliasLabelBlock.getPredecessorAt(k); - var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); - jumpOp.alive.curr[j] = register; - } - } - } - } - } - - /** - * Resolves label variable registers by finding where they are used. - */ - private void resolvePhiFromUsage() { - Queue>> worklist = new LinkedList<>(); - - // TODO: need to store paths in a more memory friendly way - // because we always copy the whole path when entering successor - // which is unnecessary and explodes in memory usage, making - // this method by far the most resource demanding, while - // not being the best. - - LinkedList> firstPath = new LinkedList<>(); - firstPath.add(this.lir.getControlFlowGraph().getStartBlock()); - worklist.add(firstPath); - - Map> labelToBlockMap = new HashMap<>(); - - Set> visited = new HashSet<>(); - while (!worklist.isEmpty()) { - var path = worklist.poll(); - var block = path.getLast(); - - if (visited.contains(block)) { - // TODO: for some reason blocks that were already visited - // are present here which causes few issues. - continue; - } - - visited.add(block); - - var instructions = this.blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - for (int i = 0; i < labelInstr.dests.count; i++) { - if (labelInstr.dests.curr[i] == null) { - Variable variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - this.labelMap.put(variable, labelInstr); - labelToBlockMap.put(labelInstr, block); - } - } - - for (var instruction : instructions) { - switch (instruction) { - case RAVInstruction.VirtualMove move -> { - if (LIRValueUtil.isVariable(move.variableOrConstant) && move.location instanceof RegisterValue register) { - this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(move.variableOrConstant), register, path, instruction); - } - } - case RAVInstruction.Op op -> { - for (int i = 0; i < op.uses.count; i++) { - if (LIRValueUtil.isVariable(op.uses.orig[i]) && op.uses.curr[i] instanceof RegisterValue register) { - this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(op.uses.orig[i]), register, path, instruction); - } - } - - if (instruction.lirInstruction instanceof StandardOp.JumpOp) { - // Always only one successor for this jump op - // Assumption here is, that we resolve aliases with original registers immediately - // so in-case an alias was defined after that happened, it's not resolved and will fail. - var label = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); - for (int i = 0; i < op.alive.count; i++) { - if (!LIRValueUtil.isVariable(op.alive.orig[i])) { - continue; - } - - var variable = LIRValueUtil.asVariable(op.alive.orig[i]); - if (this.labelMap.get(variable) != null) { - this.usageAliasMap.put(variable, LIRValueUtil.asVariable(label.dests.orig[i])); - } - } - } else { - for (int i = 0; i < op.alive.count; i++) { - if (LIRValueUtil.isVariable(op.alive.orig[i]) && op.alive.curr[i] instanceof RegisterValue register) { - this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(op.alive.orig[i]), register, path, instruction); - } - } - } - } - default -> { - } - } - } - - for (int i = 0; i < block.getSuccessorCount(); i++) { - var succ = block.getSuccessorAt(i); - if (visited.contains(succ)) { - continue; - } - - var nextPath = new LinkedList<>(path); - nextPath.add(succ); - worklist.add(nextPath); - } - } - - // We no longer throw an error when label map is not empty - // because if such thing happens, then variable was not used, - // and thus we cannot determine its location. - } - - public BlockMap> getDefinitionSets() { - BlockMap> blockDefinitions = new BlockMap<>(this.lir.getControlFlowGraph()); + public BlockMap getDefinitionSets() { + BlockMap blockDefinitions = new BlockMap<>(this.lir.getControlFlowGraph()); for (var blockId : lir.getBlocks()) { var block = lir.getBlockById(blockId); var instructions = blockInstructions.get(block); - Set definitions = new HashSet<>(); + var definitions = new DefinitionSet(); for (var instruction : instructions) { switch (instruction) { @@ -387,7 +161,7 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O if (location == null) { location = blockReg; - } else if (location.equals(blockReg)) { + } else if (!location.equals(blockReg)) { // Not same for all blocks, so none choosen. return false; } @@ -417,16 +191,10 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var propagateMap = new HashMap, List>(); var variableToRegisters = new HashMap(); var defBlockVariablesToPropagate = new LinkedList(); - // var defForEntry = this.blockDefinitions.get(defBlock); for (int i = 0; i < labelInstr.dests.count; i++) { var register = (RegisterValue) labelInstr.dests.curr[i]; var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - // var registerDefinition = defForEntry.values.get(register); - // if (registerDefinition.isUnknown()) { - // defForEntry.values.put(register, new ValueAllocationState(variable)); - // } - defBlockVariablesToPropagate.add(variable); variableToRegisters.put(variable, register); } @@ -465,10 +233,6 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { } variablesToBePropagated.add(variable); - - if (state != null) { - state.values.put(register, new ValueAllocationState(variable)); - } } if (variablesToBePropagated.isEmpty()) { @@ -748,7 +512,7 @@ public boolean verifyInstructionInputs() { public boolean run() { if (this.phiResolution == PhiResolution.FromUsage) { - this.resolvePhiFromUsage(); + this.fromUsageResolver.resolvePhiFromUsage(); } if (this.phiResolution == PhiResolution.FromPredecessors) { From b40a31da455318868ed4dcdbf977e78fd1c549f2 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 7 Jan 2026 10:51:46 +0100 Subject: [PATCH 016/112] Add readable error messages instead of asserts --- .../AliveConstraintViolationException.java | 25 +++ .../verifier/CircularDefinitionError.java | 44 ++++ .../lir/alloc/verifier/FromUsageResolver.java | 3 +- .../verifier/KindsMismatchException.java | 32 +++ .../alloc/verifier/LabelNotResolvedError.java | 45 ++++ .../verifier/MergedBlockVerifierState.java | 198 +++++++----------- .../alloc/verifier/MissingLocationError.java | 16 ++ .../verifier/PreRegisterAllocationPhase.java | 2 +- .../compiler/lir/alloc/verifier/RAVError.java | 9 + .../lir/alloc/verifier/RAVException.java | 8 + .../RAVFailedVerificationException.java | 22 ++ .../verifier/RegisterAllocationVerifier.java | 59 +++--- .../RegisterAllocationVerifierPhase.java | 48 +---- .../TargetLocationOverwrittenException.java | 19 ++ .../verifier/UnknownInstructionError.java | 30 +++ .../alloc/verifier/ValueAllocationState.java | 3 +- .../verifier/ValueNotInRegisterException.java | 37 ++++ 17 files changed, 403 insertions(+), 197 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java new file mode 100644 index 000000000000..2136e6ecbeb8 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -0,0 +1,25 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +@SuppressWarnings("serial") +public class AliveConstraintViolationException extends RAVException { + public LIRInstruction instruction; + public BasicBlock block; + + public AliveConstraintViolationException(LIRInstruction instruction, BasicBlock block, Value location, boolean asDest) { + super(AliveConstraintViolationException.getErrorMessage(instruction, block, location, asDest)); + this.instruction = instruction; + this.block = block; + } + + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value location, boolean asDest) { + if (asDest) { + return "Location " + location + " used as both alive and output in " + instruction + " in block" + block; + } + + return "Location " + location + " used as both alive and temp in " + instruction + "in block" + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java new file mode 100644 index 000000000000..cf65d4d7e1bb --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java @@ -0,0 +1,44 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.Variable; + +import java.util.List; + +@SuppressWarnings("serial") +public class CircularDefinitionError extends RAVError { + public CircularDefinitionError(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { + super(getErrorMessage(defBlock, predecessor, label, beingPropagated)); + } + + static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { + StringBuilder operandString = new StringBuilder("["); + var values = label.dests; + for (int i = 0; i < values.count; i++) { + if (LIRValueUtil.isVariable(values.orig[i])) { + continue; // Avoid fatal error + } + + var variable = LIRValueUtil.asVariable(values.orig[i]); + if (!beingPropagated.contains(variable)) { + continue; + } + + var location = values.curr[i]; + + operandString.append(variable.toString()); + if (location != null) { + operandString.append(" -> ").append(location.toString()); + } else { + operandString.append(" -> ?"); + } + + operandString.append(", "); + } + + operandString.setLength(operandString.length() - 2); + + return "Circular definition for variable detected " + predecessor + " -> " + defBlock + " with " + operandString; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java index 7b266292a18d..e1d0b0fc571b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -116,8 +116,7 @@ private Value getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVIn } case RAVInstruction.VirtualMove move -> { if (move.location.equals(location) && !move.variableOrConstant.equals(variable)) { - // TODO: change this exception - throw GraalError.shouldNotReachHere("Target register is overwritten without backup."); + throw new TargetLocationOverwrittenException(move, block); } } // For Op, if there is a redefinition, we let the later stages handle that diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java new file mode 100644 index 000000000000..03e8e751b28f --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -0,0 +1,32 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +@SuppressWarnings("serial") +public class KindsMismatchException extends RAVException { + public LIRInstruction instruction; + public BasicBlock block; + public Value value1; + public Value value2; + public boolean origVsCurr; + + public KindsMismatchException(LIRInstruction instruction, BasicBlock block, Value value1, Value value2, boolean origVsCurr) { + super(KindsMismatchException.getErrorMessage(instruction, block, value1, value2, origVsCurr)); + + this.instruction = instruction; + this.block = block; + this.value1 = value1; + this.value2 = value2; + this.origVsCurr = origVsCurr; + } + + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value value1, Value value2, boolean origVsCurr) { + if (origVsCurr) { + return value1.getValueKind() + " has different kind after allocation: " + value2.getValueKind() + " in " + instruction + " in block " + block; + } + + return "Value in location has different kind: " + value1.getValueKind() + " vs. " + value2.getValueKind() + " in " + instruction + " in block " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java new file mode 100644 index 000000000000..4f737c85d83e --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java @@ -0,0 +1,45 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; + +@SuppressWarnings("serial") +public class LabelNotResolvedError extends RAVError { + public BasicBlock block; + public RAVInstruction.Op label; + public PhiResolution resolution; + + public LabelNotResolvedError(BasicBlock block, RAVInstruction.Op label, PhiResolution resolution) { + super(LabelNotResolvedError.getErrorMessage(label)); + + this.block = block; + this.label = label; + this.resolution = resolution; + } + + static String getErrorMessage(RAVInstruction.Op label) { + StringBuilder labelStringBuilder = new StringBuilder("["); + StringBuilder unresolvedVariablesStringBuilder = new StringBuilder(); + for (int i = 0; i < label.dests.count; i++) { + var variable = label.dests.orig[i]; + var location = label.dests.curr[i]; + + labelStringBuilder.append(variable.toString()); + if (location != null) { + labelStringBuilder.append(" -> ").append(location); + } else { + labelStringBuilder.append(" -> ?"); + + unresolvedVariablesStringBuilder.append(variable); + unresolvedVariablesStringBuilder.append(", "); + } + + if (i != label.dests.count - 1) { + labelStringBuilder.append(", "); + } + } + + int unresLen = unresolvedVariablesStringBuilder.length(); + unresolvedVariablesStringBuilder.delete(unresLen - 2, unresLen); + return "Could not resolve " + unresolvedVariablesStringBuilder + ": LABEL " + labelStringBuilder + "]"; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index a55b6e6a79e1..98c34904b376 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -1,6 +1,8 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.LIRKindWithCast; +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.CastValue; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; @@ -13,7 +15,7 @@ public class MergedBlockVerifierState { protected PhiResolution phiResolution; public MergedBlockVerifierState(PhiResolution phiResolution) { - this.values = new MergedAllocationStateMap(); + this.values = new MergedAllocationStateMap(); this.phiResolution = phiResolution; } @@ -21,7 +23,7 @@ protected MergedBlockVerifierState(MergedBlockVerifierState other, PhiResolution this.phiResolution = phiResolution; if (other == null) { - this.values = new MergedAllocationStateMap(); + this.values = new MergedAllocationStateMap(); return; } @@ -36,25 +38,25 @@ public boolean meetWith(MergedBlockVerifierState other) { return this.values.mergeWith(other.getValues()); } - protected boolean checkInputs(RAVInstruction.ValueArrayPair values, boolean isJump) { + protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block, RAVInstruction.Op labelOp) { // Check that incoming values are not unknown or conflicted - these only matter if used for (int idx = 0; idx < values.count; idx++) { var orig = values.orig[idx]; var curr = values.curr[idx]; - if (curr == null && isJump && phiResolution == PhiResolution.FromUsage) { - // Whenever PhiResolution = FromUsage, variable is not used and thus no register present. - continue; - } - assert orig != null; if (curr == null) { - if (isJump) { - throw new RuntimeException(this.getMissingLabelOrJumpErrMsg("JUMP", values)); + if (op.lirInstruction instanceof StandardOp.JumpOp) { + if (phiResolution == PhiResolution.FromUsage) { + // Variable has no usage, thus no location present. + continue; + } + + throw new LabelNotResolvedError(block, labelOp, phiResolution); } - assert false; + throw new MissingLocationError(op.lirInstruction, block, orig); } if (orig.equals(curr)) { @@ -62,14 +64,13 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values, boolean isJu continue; } - AllocationState state = this.values.get(curr); - if (!kindsEqual(orig, curr)) { - return false; + throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); } + AllocationState state = this.values.get(curr); if (state.isConflicted() || state.isUnknown()) { - return false; + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } if (state instanceof ValueAllocationState valAllocState) { @@ -80,16 +81,14 @@ protected boolean checkInputs(RAVInstruction.ValueArrayPair values, boolean isJu } // Kind sizes should be checked here as well. - return false; + throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); } continue; } - throw new IllegalStateException(); // Should never reach here. + throw GraalError.shouldNotReachHere("Invalid state " + state); } - - return true; } protected boolean kindsEqual(Value orig, Value curr) { @@ -111,7 +110,7 @@ protected boolean kindsEqual(Value orig, Value curr) { return false; } - protected boolean checkAliveConstraint(RAVInstruction.Op instruction) { + protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock block) { for (int i = 0; i < instruction.alive.count; i++) { Value value = instruction.alive.curr[i]; if (Value.ILLEGAL.equals(value)) { @@ -120,31 +119,22 @@ protected boolean checkAliveConstraint(RAVInstruction.Op instruction) { for (int j = 0; j < instruction.temp.count; j++) { if (value.equals(instruction.temp.curr[j])) { - return false; + throw new AliveConstraintViolationException(instruction.lirInstruction, block, value, false); } } for (int j = 0; j < instruction.dests.count; j++) { if (value.equals(instruction.dests.curr[j])) { - return false; + throw new AliveConstraintViolationException(instruction.lirInstruction, block, value, true); } } } - - return true; } - public boolean check(RAVInstruction.Base instruction) { + public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstruction.Op labelOp) { if (instruction instanceof RAVInstruction.Op op) { - boolean isJump = op.lirInstruction instanceof StandardOp.JumpOp; - - if (!checkInputs(op.uses, isJump)) { - return false; - } - - if (!checkInputs(op.alive, isJump)) { - return false; - } + checkInputs(op.uses, op, block, labelOp); + checkInputs(op.alive, op, block, labelOp); for (int i = 0; i < op.temp.count; i++) { var curr = op.temp.curr[i]; @@ -152,111 +142,73 @@ public boolean check(RAVInstruction.Base instruction) { if (!kindsEqual(orig, curr)) { // Make sure the assigned register has the correct kind for temp. - return false; + throw new KindsMismatchException(instruction.lirInstruction, block, orig, curr, true); } } - if (!this.checkAliveConstraint(op)) { - return false; - } + this.checkAliveConstraint(op, block); } - - return true; } - public String getMissingLabelOrJumpErrMsg(String subject, RAVInstruction.ValueArrayPair values) { - String errorMsg = "["; - for (int j = 0; j < values.count; j++) { - errorMsg += values.orig[j].toString(); - if (values.curr[j] != null) { - errorMsg += " -> " + values.curr[j].toString(); - } else { - errorMsg += " -> ?"; - } - - if (j != values.count - 1) { - errorMsg += ", "; - } + public void update(RAVInstruction.Base instruction, BasicBlock block) { + switch (instruction) { + case RAVInstruction.Op op -> this.updateWithOp(op, block); + case RAVInstruction.Spill spill -> this.values.putClone(spill.to, this.values.get(spill.from)); + case RAVInstruction.Reload reload -> this.values.putClone(reload.to, this.values.get(reload.from)); + case RAVInstruction.Move move -> this.values.putClone(move.to, this.values.get(move.from)); + case RAVInstruction.StackMove move -> this.values.putClone(move.to, this.values.get(move.from)); + case RAVInstruction.VirtualMove virtMove -> this.updateWithVirtualMove(virtMove); + default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } - - return "Failed to resolve: " + subject + " " + errorMsg + "]"; } - public void update(RAVInstruction.Base instruction) { - switch (instruction) { - case RAVInstruction.Op op -> { - for (int i = 0; i < op.dests.count; i++) { - if (Value.ILLEGAL.equals(op.dests.orig[i])) { - continue; // Safe to ignore, when destination is illegal value, not when used. - } - - if ((phiResolution == PhiResolution.FromPredecessors - || phiResolution == PhiResolution.FromUsage) - && op.dests.curr[i] == null) { - // This can happen for certain instructions - jump or label, and we need to - // resolve appropriate registers for these, if we do not, we throw in check() - continue; - } - - // Here, FromJump resolution will fail if it was not completed - if (op.dests.curr[i] == null && phiResolution == PhiResolution.FromJump) { - throw new RuntimeException(this.getMissingLabelOrJumpErrMsg("LABEL", op.dests)); - } + protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { + for (int i = 0; i < op.dests.count; i++) { + if (Value.ILLEGAL.equals(op.dests.orig[i])) { + continue; // Safe to ignore, when destination is illegal value, not when used. + } - assert op.dests.curr[i] != null; - assert op.dests.orig[i] != null; + assert op.dests.orig[i] != null; - Value location = op.dests.curr[i]; - Value variable = op.dests.orig[i]; - this.values.put(location, new ValueAllocationState(variable)); + if (op.dests.curr[i] == null) { + if (phiResolution == PhiResolution.FromJump) { + throw new LabelNotResolvedError(block, op, phiResolution); } - for (int i = 0; i < op.temp.count; i++) { - var value = op.temp.curr[i]; - if (Value.ILLEGAL.equals(value)) { - continue; - } - - // We cannot believe the contents of registers used as temp, thus we need to reset. - Value location = op.temp.curr[i]; - this.values.put(location, UnknownAllocationState.INSTANCE); - } + continue; } - case RAVInstruction.Spill spill -> - this.values.putClone(spill.to, this.values.get(spill.from)); - case RAVInstruction.Reload reload -> - this.values.putClone(reload.to, this.values.get(reload.from)); - case RAVInstruction.Move move -> { - var value = this.values.get(move.from); - if (value.isUnknown()) { - // Hotfix for blockDefinitions, where if we moved a Value we need to make - // sure it's saved in the state, so that value is not propagated further - // causing Circular Exception - // TestCase: ConditionalElimination17 - value = new ValueAllocationState(Value.ILLEGAL); - } - this.values.putClone(move.to, value); - } - case RAVInstruction.StackMove move -> - this.values.putClone(move.to, this.values.get(move.from)); - case RAVInstruction.VirtualMove virtMove -> { - if (virtMove.location instanceof RegisterValue) { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); - } else if (LIRValueUtil.isVariable(virtMove.location)) { - // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD - // Move before allocation - // TODO: maybe handle this better than VirtualMove with location as Variable - // TestCase: BoxingTest.boxBoolean - var locations = this.values.getValueLocations(virtMove.variableOrConstant); - for (var location : locations) { - this.values.put(location, new ValueAllocationState(virtMove.location)); - } - } else { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); - } + Value location = op.dests.curr[i]; + Value variable = op.dests.orig[i]; + this.values.put(location, new ValueAllocationState(variable)); + } + + for (int i = 0; i < op.temp.count; i++) { + var value = op.temp.curr[i]; + if (Value.ILLEGAL.equals(value)) { + continue; } - default -> throw new IllegalStateException(); + + // We cannot believe the contents of registers used as temp, thus we need to reset. + Value location = op.temp.curr[i]; + this.values.put(location, UnknownAllocationState.INSTANCE); + } + } + + protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { + if (virtMove.location instanceof RegisterValue) { + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); + } else if (LIRValueUtil.isVariable(virtMove.location)) { + // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD + // Move before allocation + // TODO: maybe handle this better than VirtualMove with location as Variable + // TestCase: BoxingTest.boxBoolean + var locations = this.values.getValueLocations(virtMove.variableOrConstant); + for (var location : locations) { + this.values.put(location, new ValueAllocationState(virtMove.location)); + } + } else { + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java new file mode 100644 index 000000000000..cfb7063bad82 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java @@ -0,0 +1,16 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +@SuppressWarnings("serial") +public class MissingLocationError extends RAVError { + public MissingLocationError(LIRInstruction instruction, BasicBlock block, Value variable) { + super(MissingLocationError.getMessage(instruction, block, variable)); + } + + static String getMessage(LIRInstruction instruction, BasicBlock block, Value variable) { + return "Variable " + variable + " is missing a location in " + instruction + " in block " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 863771670db1..24889cc49bf3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -222,7 +222,7 @@ public BlockMap> getVerifierInstructions(LIR lir) { continue; } - throw new VerErr.UnknownInstructionError(instruction, block); + throw new UnknownInstructionError(instruction, block); } var opRAVInstr = (RAVInstruction.Op) rAVInstr; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java new file mode 100644 index 000000000000..34e15989b125 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java @@ -0,0 +1,9 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +@SuppressWarnings("serial") +public class RAVError extends Error { + // These shouldn't happen within the verifier + public RAVError(String message) { + super(message); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java new file mode 100644 index 000000000000..54b57809ca76 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java @@ -0,0 +1,8 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +@SuppressWarnings("serial") +public class RAVException extends RuntimeException { + public RAVException(String message) { + super(message); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java new file mode 100644 index 000000000000..bf1c7147ac83 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -0,0 +1,22 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import java.util.List; + +@SuppressWarnings("serial") +public class RAVFailedVerificationException extends RAVException { + public RAVFailedVerificationException(String compUnitName, List exceptions) { + super(RAVFailedVerificationException.getMessage(compUnitName, exceptions)); + } + + static String getMessage(String compUnitName, List exceptions) { + StringBuilder sb = new StringBuilder("Failed to verify "); + sb.append(compUnitName); + sb.append(":"); + for (var e : exceptions) { + sb.append(" - "); + sb.append(e.getMessage()); + sb.append("\n"); + } + return sb.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 3ab57305459e..4e19a39b17a9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -10,6 +10,7 @@ import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -98,7 +99,7 @@ public BlockMap getDefinitionSets() { case RAVInstruction.Spill spill -> definitions.add(spill.to); case RAVInstruction.StackMove stackMove -> definitions.add(stackMove.to); case RAVInstruction.VirtualMove virtMove -> definitions.add(virtMove.location); // Is this right? - default -> throw new IllegalStateException(); + default -> GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } } @@ -251,23 +252,7 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { // for example: B0 defines [v0] in label, B1 is it's successor as well as it's // predecessor, and if it does not overwrite this register, it would change // entry state for B0 to include v0, which is defined by B0. - - String errorMsg = "["; - var values = labelInstr.dests; - for (int j = 0; j < values.count; j++) { - errorMsg += values.orig[j].toString(); - if (values.curr[j] != null) { - errorMsg += " -> " + values.curr[j].toString(); - } else { - errorMsg += " -> ?"; - } - - if (j != values.count - 1) { - errorMsg += ", "; - } - } - - throw new GraalError("Circular definition for variable detected " + curr + " -> " + defBlock + " on LABEL " + errorMsg + "]"); + throw new CircularDefinitionError(defBlock, curr, labelInstr, variablesToBePropagated); } boolean dominates = succ.dominates(defBlock); @@ -398,7 +383,7 @@ public void calculateEntryBlocks() { // Create new entry state for successor blocks out of current block state var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), phiResolution); for (var instr : instructions) { - state.update(instr); + state.update(instr, block); } this.blockStates.put(block, state); @@ -474,7 +459,7 @@ private void addMissingLabelBlocks(Queue> worklist) { var label = (RAVInstruction.Op) this.blockInstructions.get(pBlock).getFirst(); if (this.hasMissingRegistersInLabel(label)) { if (!this.doPrecessorsHaveStates(pBlock)) { - throw new RuntimeException("Could not determine label registers for " + pBlock); + throw new LabelNotResolvedError(pBlock, label, phiResolution); } currentMissingLabelBlockCount++; @@ -493,24 +478,44 @@ private void addMissingLabelBlocks(Queue> worklist) { * * @return true, if valid, otherwise false */ - public boolean verifyInstructionInputs() { + public void verifyInstructionInputs() { for (var blockId : this.lir.getBlocks()) { var block = this.lir.getBlockById(blockId); var state = this.blockEntryStates.get(block); var instructions = this.blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); for (var instr : instructions) { - if (!state.check(instr)) { - return false; + state.check(instr, block, labelInstr); + state.update(instr, block); + } + } + } + + public void verifyInstructionsAndCollectErrors(String compUnitName) { + List exceptions = new ArrayList<>(); + for (var blockId : this.lir.getBlocks()) { + var block = this.lir.getBlockById(blockId); + var state = this.blockEntryStates.get(block); + var instructions = this.blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + + for (var instr : instructions) { + try { + state.check(instr, block, labelInstr); + state.update(instr, block); + } catch (RAVException e) { + exceptions.add(e); } - state.update(instr); } } - return true; + if (!exceptions.isEmpty()) { + throw new RAVFailedVerificationException(compUnitName, exceptions); + } } - public boolean run() { + public void run() { if (this.phiResolution == PhiResolution.FromUsage) { this.fromUsageResolver.resolvePhiFromUsage(); } @@ -523,6 +528,6 @@ public boolean run() { } this.calculateEntryBlocks(); - return this.verifyInstructionInputs(); + this.verifyInstructionInputs(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 1de81ea27c31..983df0e50a00 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -1,39 +1,13 @@ package jdk.graal.compiler.lir.alloc.verifier; -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.ValueProcedure; -import jdk.graal.compiler.lir.Variable; -import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; -import jdk.graal.compiler.lir.gen.LIRGenerator; import jdk.graal.compiler.lir.phases.AllocationPhase; import jdk.graal.compiler.options.EnumOptionKey; 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.RegisterValue; -import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.meta.Value; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; public class RegisterAllocationVerifierPhase extends AllocationPhase { public static class Options { @@ -50,16 +24,7 @@ public static class Options { public static String[] ignoredTestCases = { // Disable Truffle Related Tests // New instructions being added makes the verifier not work, investigate why prealloc is not run. - "truffle", - "Truffle", - "polyglot", - "Polyglot", - "Root[]", - "InstrumentationTestLanguage", - "NFITest", - "intCaller1", - "callee" - }; + "truffle", "Truffle", "polyglot", "Polyglot", "Root[]", "InstrumentationTestLanguage", "NFITest", "intCaller1", "callee"}; public static boolean isIgnored(String compUnitName) { for (String ignoredTest : ignoredTestCases) { @@ -79,7 +44,6 @@ public RegisterAllocationVerifierPhase(OptionValues options) { } protected PreRegisterAllocationPhase preallocPhaseRAVerifier; - // protected RAVerifierPreAllocPhase preallocPhaseRAVerifier; public AllocationPhase getPreAllocPhase() { this.preallocPhaseRAVerifier = new PreRegisterAllocationPhase(phiResolution, moveConstants); @@ -89,7 +53,7 @@ public AllocationPhase getPreAllocPhase() { @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { var compUnitName = lirGenRes.getCompilationUnitName(); - // if (!compUnitName.contains("nestedSum")) { + // if (!compUnitName.contains("DirectByteBufferTest.unalignedWriteSnippet")) { // return; // } @@ -102,14 +66,12 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo assert this.preallocPhaseRAVerifier != null : "Phase before register allocation was not run, cannot verify it."; - // var instructions = this.getVerifierInstructions(lirGenRes.getLIR()); var instructions = this.preallocPhaseRAVerifier.getVerifierInstructions(lirGenRes.getLIR()); var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.phiResolution); - if (!verifier.run()) { - // TODO: every error generated by the verifier needs to be a lot more verbose. - // throw new IllegalStateException("Could not verify the register allocation..."); - // Exception handler + try { + verifier.run(); + } catch (RAVException e) { System.err.println("Verification failed - " + compUnitName); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java new file mode 100644 index 000000000000..86511548c979 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java @@ -0,0 +1,19 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; + +@SuppressWarnings("serial") +public class TargetLocationOverwrittenException extends RAVException { + public RAVInstruction.VirtualMove virtualMove; + public BasicBlock block; + + public TargetLocationOverwrittenException(RAVInstruction.VirtualMove move, BasicBlock block) { + super(getErrorMessage(move, block)); + this.virtualMove = move; + this.block = block; + } + + static String getErrorMessage(RAVInstruction.VirtualMove move, BasicBlock block) { + return "Target location " + move.location + " was overwritten by " + move.lirInstruction + " in " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java new file mode 100644 index 000000000000..006c73d0722c --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java @@ -0,0 +1,30 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.StandardOp; + +@SuppressWarnings("serial") +public class UnknownInstructionError extends RAVError { + public LIRInstruction instruction; + public BasicBlock block; + + public UnknownInstructionError(LIRInstruction instruction, BasicBlock block) { + super(UnknownInstructionError.getErrorMessage(instruction, block)); + + this.instruction = instruction; + this.block = block; + } + + static String getErrorMessage(LIRInstruction instruction, BasicBlock block) { + if (instruction.isMoveOp()) { + return "Unknown MOVE " + instruction + " in " + block; + } + + if (instruction instanceof StandardOp.LabelOp) { + return "Unknown LABEL " + instruction + " in " + block; + } + + return "Unknown instruction for RAV " + instruction + " in " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 221494f401ef..b8c331820070 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -1,5 +1,6 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.VirtualStackSlot; @@ -21,7 +22,7 @@ public ValueAllocationState(Value value) { // TODO: reconsider handling of StackSlots this.value = value; } else { - throw new IllegalStateException(); + throw GraalError.shouldNotReachHere("Invalid type of value used " + value); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java new file mode 100644 index 000000000000..12c261d1da8a --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -0,0 +1,37 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +@SuppressWarnings("serial") +public class ValueNotInRegisterException extends RAVException { + public LIRInstruction instruction; + public BasicBlock block; + public Value variable; // Can be a constant or other symbolic value + public Value location; // Can be StackSlot, RegisterValue or memory + public AllocationState state; + + public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { + super(ValueNotInRegisterException.getErrorMessage(instruction, block, variable, location, state)); + + this.instruction = instruction; + this.block = block; + this.variable = variable; + this.location = location; + this.state = state; + } + + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { + return "Value " + + variable + + " not found in " + + location + + " for instruction " + + instruction + + " in block " + + block + + " actual state is " + + state; + } +} From f48e6d5b795588360ae9160ca65e2a60fb7f4975 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 7 Jan 2026 11:18:37 +0100 Subject: [PATCH 017/112] Check if register can be allocated --- .../lir/alloc/verifier/FromUsageResolver.java | 2 +- .../verifier/MergedBlockVerifierState.java | 37 +++++++++++++++++-- .../verifier/RegisterAllocationVerifier.java | 18 ++++++--- .../RegisterAllocationVerifierPhase.java | 2 +- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java index e1d0b0fc571b..18039ec27a10 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -207,7 +207,7 @@ private void tryMappingVariable( return; } - if (location instanceof Variable || location instanceof ConstantValue) { + if (LIRValueUtil.isVariable(location) || location instanceof ConstantValue) { return; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 98c34904b376..5782dc5319b2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -1,26 +1,31 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.LIRKindWithCast; +import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.CastValue; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; public class MergedBlockVerifierState { public MergedAllocationStateMap values; protected PhiResolution phiResolution; + protected RegisterAllocationConfig registerAllocationConfig; - public MergedBlockVerifierState(PhiResolution phiResolution) { + public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution) { this.values = new MergedAllocationStateMap(); this.phiResolution = phiResolution; + this.registerAllocationConfig = registerAllocationConfig; } - protected MergedBlockVerifierState(MergedBlockVerifierState other, PhiResolution phiResolution) { + protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution) { this.phiResolution = phiResolution; + this.registerAllocationConfig = registerAllocationConfig; if (other == null) { this.values = new MergedAllocationStateMap(); @@ -154,14 +159,32 @@ public void update(RAVInstruction.Base instruction, BasicBlock block) { switch (instruction) { case RAVInstruction.Op op -> this.updateWithOp(op, block); case RAVInstruction.Spill spill -> this.values.putClone(spill.to, this.values.get(spill.from)); - case RAVInstruction.Reload reload -> this.values.putClone(reload.to, this.values.get(reload.from)); - case RAVInstruction.Move move -> this.values.putClone(move.to, this.values.get(move.from)); + case RAVInstruction.Reload reload -> { + this.checkRegisterDestinationValidity(reload.to); + this.values.putClone(reload.to, this.values.get(reload.from)); + } + case RAVInstruction.Move move -> { + this.checkRegisterDestinationValidity(move.to); + this.values.putClone(move.to, this.values.get(move.from)); + } case RAVInstruction.StackMove move -> this.values.putClone(move.to, this.values.get(move.from)); case RAVInstruction.VirtualMove virtMove -> this.updateWithVirtualMove(virtMove); default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } } + protected void checkRegisterDestinationValidity(Value location) { + if (!ValueUtil.isRegister(location)) { + return; + } + + // Equality check so we know that this change was made by the register allocator. + var register = ValueUtil.asRegister(location); + if (!this.registerAllocationConfig.getAllocatableRegisters().contains(register)) { + throw new InvalidRegisterUsedException(register); + } + } + protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { for (int i = 0; i < op.dests.count; i++) { if (Value.ILLEGAL.equals(op.dests.orig[i])) { @@ -180,6 +203,12 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { Value location = op.dests.curr[i]; Value variable = op.dests.orig[i]; + + if (!location.equals(variable)) { + // Equality check so we know that this change was made by the register allocator. + this.checkRegisterDestinationValidity(location); + } + this.values.put(location, new ValueAllocationState(variable)); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 4e19a39b17a9..cc0c3d3a7db3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -1,5 +1,6 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.GraalError; @@ -37,13 +38,15 @@ public final class RegisterAllocationVerifier { public FromUsageResolver fromUsageResolver; - public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution) { + protected RegisterAllocationConfig registerAllocationConfig; + + public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); - this.blockEntryStates.put(cfg.getStartBlock(), new MergedBlockVerifierState(phiResolution)); + this.blockEntryStates.put(cfg.getStartBlock(), new MergedBlockVerifierState(registerAllocationConfig, phiResolution)); this.blockStates = new BlockMap<>(cfg); this.phiResolution = phiResolution; @@ -54,6 +57,7 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.usageAliasMap = new HashMap<>(); this.fromUsageResolver = new FromUsageResolver(lir, blockInstructions); + this.registerAllocationConfig = registerAllocationConfig; } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -98,7 +102,11 @@ public BlockMap getDefinitionSets() { case RAVInstruction.Reload reload -> definitions.add(reload.to); case RAVInstruction.Spill spill -> definitions.add(spill.to); case RAVInstruction.StackMove stackMove -> definitions.add(stackMove.to); - case RAVInstruction.VirtualMove virtMove -> definitions.add(virtMove.location); // Is this right? + case RAVInstruction.VirtualMove virtMove -> { + if (!LIRValueUtil.isVariable(virtMove.location)) { + definitions.add(virtMove.location); + } + } default -> GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } } @@ -381,7 +389,7 @@ public void calculateEntryBlocks() { } // Create new entry state for successor blocks out of current block state - var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), phiResolution); + var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), registerAllocationConfig, phiResolution); for (var instr : instructions) { state.update(instr, block); } @@ -400,7 +408,7 @@ public void calculateEntryBlocks() { MergedBlockVerifierState succState; if (this.blockEntryStates.get(succ) == null) { - succState = new MergedBlockVerifierState(phiResolution); + succState = new MergedBlockVerifierState(registerAllocationConfig, phiResolution); // Either there's no state because it was not yet processed first part of the condition // or, we need to reset it because label changed, second part of the condition diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 983df0e50a00..d2be186d3dfb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -67,7 +67,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo assert this.preallocPhaseRAVerifier != null : "Phase before register allocation was not run, cannot verify it."; var instructions = this.preallocPhaseRAVerifier.getVerifierInstructions(lirGenRes.getLIR()); - var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.phiResolution); + var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.phiResolution, context.registerAllocationConfig); try { verifier.run(); From 6e8dda9c99ae8267f101a0260f7c58ddeb856ec0 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 7 Jan 2026 12:25:05 +0100 Subject: [PATCH 018/112] Add missing exception --- .../verifier/InvalidRegisterUsedException.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java new file mode 100644 index 000000000000..c4446d5710ee --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java @@ -0,0 +1,15 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.vm.ci.code.Register; + +@SuppressWarnings("serial") +public class InvalidRegisterUsedException extends RAVException { + + public InvalidRegisterUsedException(Register register) { + super(getErrorMessage(register)); + } + + static String getErrorMessage(Register register) { + return "Register " + register + " is not allowed to be used by RegisterAllocatorConfig"; + } +} From d84dd1844f482fa28e2692e62fe13c7e591814c5 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 16:30:47 +0100 Subject: [PATCH 019/112] Use correct label instruction for label resolution error --- .../lir/alloc/verifier/RegisterAllocationVerifier.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index cc0c3d3a7db3..ea2866821010 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -491,10 +491,14 @@ public void verifyInstructionInputs() { var block = this.lir.getBlockById(blockId); var state = this.blockEntryStates.get(block); var instructions = this.blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + + RAVInstruction.Op labelInstrOfSucc = null; + if (block.getSuccessorCount() == 1) { + labelInstrOfSucc = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); + } for (var instr : instructions) { - state.check(instr, block, labelInstr); + state.check(instr, block, labelInstrOfSucc); state.update(instr, block); } } From c767c1564b9fda4e9fc2088b116be9a50c3ca213 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 16:51:21 +0100 Subject: [PATCH 020/112] Fix logic bug in CircularDefinitionError --- .../compiler/lir/alloc/verifier/CircularDefinitionError.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java index cf65d4d7e1bb..e888d6aa90ff 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java @@ -16,7 +16,7 @@ static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, StringBuilder operandString = new StringBuilder("["); var values = label.dests; for (int i = 0; i < values.count; i++) { - if (LIRValueUtil.isVariable(values.orig[i])) { + if (!LIRValueUtil.isVariable(values.orig[i])) { continue; // Avoid fatal error } @@ -38,6 +38,7 @@ static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, } operandString.setLength(operandString.length() - 2); + operandString.append("]"); return "Circular definition for variable detected " + predecessor + " -> " + defBlock + " with " + operandString; } From d260760415629bd905b15bc3bac04949f74a5456 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 16:51:37 +0100 Subject: [PATCH 021/112] Fix trailing comma in LabelNotResolvedError --- .../compiler/lir/alloc/verifier/LabelNotResolvedError.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java index 4f737c85d83e..9c05fd638d91 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java @@ -33,13 +33,12 @@ static String getErrorMessage(RAVInstruction.Op label) { unresolvedVariablesStringBuilder.append(", "); } - if (i != label.dests.count - 1) { - labelStringBuilder.append(", "); - } + labelStringBuilder.append(", "); } int unresLen = unresolvedVariablesStringBuilder.length(); unresolvedVariablesStringBuilder.delete(unresLen - 2, unresLen); + return "Could not resolve " + unresolvedVariablesStringBuilder + ": LABEL " + labelStringBuilder + "]"; } } From a3bd670bf83d5dcaebd53cd6fe8192975b0f9d0b Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 16:52:54 +0100 Subject: [PATCH 022/112] Add string representation for RAVInstruction --- .../lir/alloc/verifier/RAVInstruction.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 88d17ee04f8f..d0a7fe8a9514 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -130,6 +130,27 @@ public boolean verifyContents() { } return true; } + + @Override + public String toString() { + StringBuilder result = new StringBuilder("["); + for (int i = 0; i < this.count; i++) { + if (this.curr[i] != null) { + result.append(this.orig[i].toString()).append(" -> ").append(this.curr[i].toString()); + } else { + result.append(this.orig[i].toString()); + } + + result.append(", "); + } + + if (this.count > 0) { + result.setLength(result.length() - 2); + } + + result.append("]"); + return result.toString(); + } } public static class Op extends Base { @@ -164,6 +185,11 @@ public boolean hasMissingDefinitions() { } return false; } + + @Override + public String toString() { + return this.dests.toString() + " = Op " + this.uses.toString() + " " + this.alive.toString() + " " + this.temp.toString(); + } } public static class Move extends Base { @@ -175,6 +201,10 @@ public Move(LIRInstruction instr, RegisterValue from, RegisterValue to) { this.from = from; this.to = to; } + + public String toString() { + return to.toString() + " = MOVE " + from.toString(); + } } // StackMove class to handle STACKMOVE instruction, temporary for now @@ -192,6 +222,10 @@ public StackMove(LIRInstruction instr, Value from, Value to) { this.from = from; this.to = to; } + + public String toString() { + return to.toString() + " = MOVE " + from.toString(); + } } public static class Reload extends Base { @@ -203,6 +237,10 @@ public Reload(LIRInstruction instr, RegisterValue to, Value from) { this.from = from; this.to = to; } + + public String toString() { + return to.toString() + " = RELOAD " + from.toString(); + } } public static class Spill extends Base { @@ -214,6 +252,10 @@ public Spill(LIRInstruction instr, Value to, RegisterValue from) { this.to = to; this.from = from; } + + public String toString() { + return to.toString() + " = SPILL " + from.toString(); + } } public static class VirtualMove extends Base { @@ -225,5 +267,9 @@ public VirtualMove(LIRInstruction instr, Value variableOrConstant, Value locatio this.variableOrConstant = variableOrConstant; this.location = location; } + + public String toString() { + return location.toString() + " = VIRTMOVE " + variableOrConstant.toString(); + } } } From 92c489ae2b8971ff2636e021ffdc51396b984cde Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 16:56:43 +0100 Subject: [PATCH 023/112] Propagate variables with more accuracy --- .../verifier/RegisterAllocationVerifier.java | 182 ++++++++++++++++-- 1 file changed, 161 insertions(+), 21 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index ea2866821010..5da59dd38087 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -7,6 +7,7 @@ import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; @@ -14,6 +15,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -193,32 +195,80 @@ private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.O return true; } + class VariableLocations implements Iterable { + protected Set internalList; + protected Map valueMap; + + public VariableLocations() { + this.internalList = new HashSet<>(); + this.valueMap = new HashMap<>(); + } + + public VariableLocations(VariableLocations other) { + this.internalList = new HashSet<>(other.internalList); + this.valueMap = new HashMap<>(other.valueMap); + } + + public void add(Value location) { + var locString = getValueKeyString(location); + internalList.add(locString); + valueMap.put(locString, location); + } + + public void remove(Value location) { + var locString = getValueKeyString(location); + internalList.remove(locString); + valueMap.remove(locString); + } + + public boolean isEmpty() { + return internalList.isEmpty(); + } + + public boolean contains(Value location) { + var locString = getValueKeyString(location); + return valueMap.containsKey(locString); + } + + protected String getValueKeyString(Value value) { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + return value.toString(); + } + + @Override + public Iterator iterator() { + return internalList.stream().map(valueMap::get).iterator(); + } + } + private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); // Definition block needs to have this set. var propagateMap = new HashMap, List>(); - var variableToRegisters = new HashMap(); + var locationMap = new HashMap, Map>(); + + var defVariableToLocations = new HashMap(); var defBlockVariablesToPropagate = new LinkedList(); for (int i = 0; i < labelInstr.dests.count; i++) { - var register = (RegisterValue) labelInstr.dests.curr[i]; + var register = labelInstr.dests.curr[i]; var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); defBlockVariablesToPropagate.add(variable); - variableToRegisters.put(variable, register); + + var variableLocationList = new VariableLocations(); + variableLocationList.add(register); + defVariableToLocations.put(variable, variableLocationList); } Queue> worklist = new LinkedList<>(); Set> processed = new HashSet<>(); worklist.add(defBlock); propagateMap.put(defBlock, defBlockVariablesToPropagate); - - // Monitor_notowner01 + Moniitor_contended01 - // B23 216 stack:48|QWORD[.] = MOVE input: rsi|QWORD[.] moveKind: QWORD // LSRAEliminateSpillMove: store at definition - // old variable supposed to be overwriten stored in spillSpill slots... - // TODO: any location we store the new variables in, needs to be propagated as well - // and this can be done in successors and needs to be accounted for... - // TODO: Reconsider any move with label variable. + locationMap.put(defBlock, defVariableToLocations); while (!worklist.isEmpty()) { var curr = worklist.remove(); @@ -227,21 +277,89 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { } processed.add(curr); - var def = this.blockDefinitions.get(curr); var state = this.blockStates.get(curr); - var variablesToPropagate = propagateMap.get(curr); - var itToPropagate = variablesToPropagate.iterator(); - var variablesToBePropagated = new LinkedList(); - while (itToPropagate.hasNext()) { - var variable = LIRValueUtil.asVariable(itToPropagate.next()); - var register = variableToRegisters.get(variable); + var variableToLocations = locationMap.get(curr); - if (def.contains(register)) { + var instructions = blockInstructions.get(curr); + for (var instruction : instructions) { + if (curr.equals(defBlock) && instruction.lirInstruction instanceof StandardOp.LabelOp) { + continue; + } + + Value fromLocation; + Value toLocation; + + switch (instruction) { + case RAVInstruction.Move move -> { + fromLocation = move.from; + toLocation = move.to; + } + case RAVInstruction.Spill spill -> { + fromLocation = spill.from; + toLocation = spill.to; + } + case RAVInstruction.StackMove stack -> { + fromLocation = stack.from; + toLocation = stack.to; + } + case RAVInstruction.Reload reload -> { + fromLocation = reload.from; + toLocation = reload.to; + } + case RAVInstruction.VirtualMove virtMove -> { + toLocation = virtMove.location; + fromLocation = null; + } + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + var location = op.dests.curr[i]; + if (location == null) { + continue; + } + + var itToPropagate = variablesToPropagate.iterator(); + while (itToPropagate.hasNext()) { + var variable = LIRValueUtil.asVariable(itToPropagate.next()); + var locations = variableToLocations.get(variable); + locations.remove(location); + } + } + + continue; + } + default -> { + continue; + } + } + + var itToPropagate = variablesToPropagate.iterator(); + while (itToPropagate.hasNext()) { + var variable = LIRValueUtil.asVariable(itToPropagate.next()); + var locations = variableToLocations.get(variable); + if (fromLocation != null && locations.contains(fromLocation)) { + locations.add(toLocation); + } else if (locations.contains(toLocation)) { + locations.remove(toLocation); // Overwritten + } + } + } + + var variablesToBePropagated = new LinkedList(); + var iterator = variablesToPropagate.iterator(); + while (iterator.hasNext()) { + var variable = LIRValueUtil.asVariable(iterator.next()); + var locations = variableToLocations.get(variable); + if (locations.isEmpty()) { continue; } variablesToBePropagated.add(variable); + for (var location : locations) { + if (state != null) { + state.values.put(location, new ValueAllocationState(variable)); + } + } } if (variablesToBePropagated.isEmpty()) { @@ -260,7 +378,23 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { // for example: B0 defines [v0] in label, B1 is it's successor as well as it's // predecessor, and if it does not overwrite this register, it would change // entry state for B0 to include v0, which is defined by B0. - throw new CircularDefinitionError(defBlock, curr, labelInstr, variablesToBePropagated); + + for (int j = 0; j < labelInstr.dests.count; j++) { + var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[j]); + + if (!variablesToPropagate.contains(variable)) { + continue; + } + + var location = labelInstr.dests.curr[j]; + var locations = variableToLocations.get(variable); + if (locations.contains(location)) { + // Only throw this error if location in label was not overwritten. + throw new CircularDefinitionError(defBlock, curr, labelInstr, variablesToBePropagated); + } + } + + continue; } boolean dominates = succ.dominates(defBlock); @@ -268,13 +402,19 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { continue; } + Map newLoc = new HashMap<>(); var itToBePropagated = variablesToBePropagated.iterator(); while (itToBePropagated.hasNext()) { var variable = LIRValueUtil.asVariable(itToBePropagated.next()); - var register = variableToRegisters.get(variable); - succEntryState.values.put(register, new ValueAllocationState(variable)); + var locations = variableToLocations.get(variable); + for (var location : locations) { + succEntryState.values.put(location, new ValueAllocationState(variable)); + } + + newLoc.put(variable, new VariableLocations(locations)); } + locationMap.put(succ, newLoc); propagateMap.put(succ, variablesToBePropagated); worklist.add(succ); } From 37b76d4a15a1cda0cc9b18b68e8efebaa1cc9b59 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 17:48:47 +0100 Subject: [PATCH 024/112] Add source to ValueAllocationState --- .../verifier/ConflictedAllocationState.java | 39 ++++++++++--------- .../verifier/MergedAllocationStateMap.java | 8 ++-- .../verifier/MergedBlockVerifierState.java | 8 ++-- .../verifier/RegisterAllocationVerifier.java | 4 +- .../alloc/verifier/ValueAllocationState.java | 12 +++++- .../verifier/ValueNotInRegisterException.java | 34 +++++++++++----- 6 files changed, 65 insertions(+), 40 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index f31df6a21065..6f245ab36b4a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -1,30 +1,32 @@ package jdk.graal.compiler.lir.alloc.verifier; -import jdk.vm.ci.meta.Value; - import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class ConflictedAllocationState extends AllocationState { - protected Set conflictedValues; + protected Set conflictedStates; + + public ConflictedAllocationState() { + this.conflictedStates = new HashSet<>(); + } - public ConflictedAllocationState(Value value1, Value value2) { - this.conflictedValues = new HashSet<>(); - this.conflictedValues.add(value1); - this.conflictedValues.add(value2); + public ConflictedAllocationState(ValueAllocationState state1, ValueAllocationState state2) { + this(); + this.conflictedStates.add(state1); // Not using addConflictedValue because a warning is thrown + this.conflictedStates.add(state2); } - private ConflictedAllocationState(Set conflictedValues) { - this.conflictedValues = new HashSet<>(conflictedValues); + private ConflictedAllocationState(Set conflictedStates) { + this.conflictedStates = new HashSet<>(conflictedStates); } - public void addConflictedValue(Value value) { - this.conflictedValues.add(value); + public void addConflictedValue(ValueAllocationState state) { + this.conflictedStates.add(state); } - public Set getConflictedValues() { - return this.conflictedValues; + public Set getConflictedStates() { + return this.conflictedStates; } @Override @@ -34,20 +36,21 @@ public boolean isConflicted() { @Override public AllocationState meet(AllocationState other) { + var newlyConflictedState = new ConflictedAllocationState(this.getConflictedStates()); if (other instanceof ValueAllocationState valueState) { - this.addConflictedValue(valueState.getValue()); + newlyConflictedState.addConflictedValue(valueState); } if (other instanceof ConflictedAllocationState conflictedState) { - this.conflictedValues.addAll(conflictedState.conflictedValues); + newlyConflictedState.conflictedStates.addAll(conflictedState.conflictedStates); } - return this; + return newlyConflictedState; } @Override public AllocationState clone() { - return new ConflictedAllocationState(this.conflictedValues); + return new ConflictedAllocationState(this.conflictedStates); } @Override @@ -57,6 +60,6 @@ public boolean equals(AllocationState other) { @Override public String toString() { - return "Conflicted {" + Arrays.toString(this.conflictedValues.toArray()) + "}"; + return "Conflicted {" + Arrays.toString(this.conflictedStates.toArray()) + "}"; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index a58fa7e058df..69fbad530d02 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -13,7 +13,7 @@ public class MergedAllocationStateMap { /** * These are instances of Value we need to keep for getValueLocations, * indexed by their string representation. - * + *

* Because Values have their own toString implementation, * that the hash map uses and for some Values we do not * want this (especially due to kinds), which are irrelevant @@ -25,11 +25,11 @@ public class MergedAllocationStateMap { protected Map locationTimings; /** * Prioritized locations are ones made by the register allocator itself. - * + *

* Whenever we are resolving phi variables, these are prioritized * because they are likely what the register allocator chose * to be used as phi locations. - * + *

* If there's multiple, then time should make the difference. */ protected Map prioritizedLocations; @@ -117,7 +117,7 @@ public void put(Value key, AllocationState state) { } public void putClone(Value key, AllocationState value) { - if (value.isConflicted() || value.isUnknown()) { + if (value.isUnknown()) { this.put(key, value); return; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 5782dc5319b2..33e3f4b8a5f2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -209,7 +209,7 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { this.checkRegisterDestinationValidity(location); } - this.values.put(location, new ValueAllocationState(variable)); + this.values.put(location, new ValueAllocationState(variable, op.lirInstruction)); } for (int i = 0; i < op.temp.count; i++) { @@ -226,7 +226,7 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { if (virtMove.location instanceof RegisterValue) { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove.lirInstruction)); } else if (LIRValueUtil.isVariable(virtMove.location)) { // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD // Move before allocation @@ -234,10 +234,10 @@ protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { // TestCase: BoxingTest.boxBoolean var locations = this.values.getValueLocations(virtMove.variableOrConstant); for (var location : locations) { - this.values.put(location, new ValueAllocationState(virtMove.location)); + this.values.put(location, new ValueAllocationState(virtMove.location, virtMove.lirInstruction)); } } else { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant)); + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove.lirInstruction)); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 5da59dd38087..b1910c4bc5ba 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -357,7 +357,7 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { variablesToBePropagated.add(variable); for (var location : locations) { if (state != null) { - state.values.put(location, new ValueAllocationState(variable)); + state.values.put(location, new ValueAllocationState(variable, labelInstr.lirInstruction)); } } } @@ -408,7 +408,7 @@ private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var variable = LIRValueUtil.asVariable(itToBePropagated.next()); var locations = variableToLocations.get(variable); for (var location : locations) { - succEntryState.values.put(location, new ValueAllocationState(variable)); + succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr.lirInstruction)); } newLoc.put(variable, new VariableLocations(locations)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index b8c331820070..562fa0143fff 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -2,6 +2,7 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.vm.ci.code.RegisterValue; @@ -10,8 +11,9 @@ public class ValueAllocationState extends AllocationState implements Cloneable { protected Value value; + protected LIRInstruction source; - public ValueAllocationState(Value value) { + public ValueAllocationState(Value value, LIRInstruction source) { if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { // StackSlot, RegisterValue is present in start block in label as predefined argument // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS @@ -21,6 +23,7 @@ public ValueAllocationState(Value value) { // but real registers can also be used as that, in some cases. // TODO: reconsider handling of StackSlots this.value = value; + this.source = source; } else { throw GraalError.shouldNotReachHere("Invalid type of value used " + value); } @@ -45,7 +48,7 @@ public AllocationState meet(AllocationState other) { var otherValueAllocState = (ValueAllocationState) other; if (!this.value.equals(otherValueAllocState.getValue())) { - return new ConflictedAllocationState(this.value, otherValueAllocState.getValue()); + return new ConflictedAllocationState(this, otherValueAllocState); } return this; @@ -65,4 +68,9 @@ public ValueAllocationState clone() { public String toString() { return "Value {" + this.value + "}"; } + + @Override + public int hashCode() { + return this.value.hashCode(); + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 12c261d1da8a..74b56fc974db 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -23,15 +23,29 @@ public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock blo } static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { - return "Value " + - variable + - " not found in " + - location + - " for instruction " + - instruction + - " in block " + - block + - " actual state is " + - state; + var messageBuilder = new StringBuilder(); + messageBuilder + .append("Value ") + .append(variable) + .append(" not found in ") + .append(location) + .append("for instruction ") + .append(instruction) + .append(" in ") + .append(block) + .append(" actual state is "); + + if (state instanceof ConflictedAllocationState confState) { + var confStates = confState.getConflictedStates(); + + messageBuilder.append("\n"); + for (var conflictedState : confStates) { + messageBuilder.append(" - ").append(conflictedState.getValue()).append(" from ").append(conflictedState.source).append("\n"); + } + } else { + messageBuilder.append(state); + } + + return messageBuilder.toString(); } } From 3f7060e646e022fae04d00400bd0d7cc2342c2a0 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 17:52:33 +0100 Subject: [PATCH 025/112] Resolve materialized constants to variables --- .../verifier/MergedBlockVerifierState.java | 76 +++++++++++++++++-- .../verifier/PreRegisterAllocationPhase.java | 23 ++++-- .../verifier/RegisterAllocationVerifier.java | 12 ++- 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 33e3f4b8a5f2..58ae800ccfa7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -5,27 +5,35 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.CastValue; +import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; +import java.util.Map; + public class MergedBlockVerifierState { public MergedAllocationStateMap values; protected PhiResolution phiResolution; protected RegisterAllocationConfig registerAllocationConfig; - public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution) { + protected Map constantVariableMap; + + public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, Map constantVariableMap) { this.values = new MergedAllocationStateMap(); this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; + this.constantVariableMap = constantVariableMap; } - protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution) { + protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, Map constantVariableMap) { this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; + this.constantVariableMap = constantVariableMap; if (other == null) { this.values = new MergedAllocationStateMap(); @@ -74,19 +82,77 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } AllocationState state = this.values.get(curr); - if (state.isConflicted() || state.isUnknown()) { + if (state.isUnknown()) { throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } + if (state.isConflicted()) { + var conflictedState = (ConflictedAllocationState) state; + + var confStates = conflictedState.getConflictedStates(); + + Variable variable = null; + ConstantValue constantValue = null; + + for (var states : confStates) { + var value = states.getValue(); + if (LIRValueUtil.isVariable(value)) { + if (variable != null && !variable.equals(value)) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + variable = LIRValueUtil.asVariable(value); + } else if (value instanceof ConstantValue constValue) { + if (constantValue != null && !constantValue.equals(constValue)) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + constantValue = constValue; + } + } + + if (variable == null || constantValue == null) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + if (!this.constantVariableMap.containsKey(constantValue)) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + if (!this.constantVariableMap.get(constantValue).contains(variable)) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + this.values.put(curr, new ValueAllocationState(variable, op.lirInstruction)); + continue; + } + if (state instanceof ValueAllocationState valAllocState) { + if (!kindsEqual(orig, valAllocState.value) && !(op.lirInstruction instanceof StandardOp.JumpOp)) { + if (!(orig instanceof CastValue castOrig && kindsEqual(castOrig.underlyingValue(), valAllocState.value))) { + // If orig was recast, it does not matter for this validation + // as we just check that the type before is correct + // TODO: sort this code better + throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); + } + } + if (!valAllocState.value.equals(orig)) { if (orig instanceof CastValue castValue && valAllocState.value.equals(castValue.underlyingValue())) { // check for underlying value for CastValue. continue; } - // Kind sizes should be checked here as well. - throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); + if (valAllocState.value instanceof ConstantValue constValue) { + if (!this.constantVariableMap.get(constValue).contains(orig)) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + this.values.put(curr, new ValueAllocationState(orig, op.lirInstruction)); + continue; + } + + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } continue; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 24889cc49bf3..fd08ce2bc7f6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -37,12 +37,14 @@ public class PreRegisterAllocationPhase extends AllocationPhase { protected PhiResolution phiResolution; protected TaggedConstantFactory taggedConstantFactory; protected boolean moveConstants; + public Map constantVariableMap; protected PreRegisterAllocationPhase(PhiResolution phiResolution, boolean moveConstants) { this.preallocMap = new HashMap<>(); this.phiResolution = phiResolution; this.taggedConstantFactory = new TaggedConstantFactory(); this.moveConstants = moveConstants; + this.constantVariableMap = new HashMap<>(); } public static class ConstantOverrideValueProcedure implements ValueProcedure { @@ -105,11 +107,6 @@ public Map getPreallocMap() { @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - var compUnitName = lirGenRes.getCompilationUnitName(); - if (RegisterAllocationVerifierPhase.isIgnored(compUnitName)) { - return; - } - LIR lir = lirGenRes.getLIR(); Map constantValueMap = new HashMap<>(); @@ -175,6 +172,22 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo this.preallocMap.put(instruction, opRAVInstr); + if (instruction.isLoadConstantOp()) { + var constOp = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); + var input = constOp.getConstant(); + var output = constOp.getResult(); + + if (LIRValueUtil.isVariable(output)) { + var variable = LIRValueUtil.asVariable(output); + var constValue = new ConstantValue(variable.getValueKind(), input); + if (!this.constantVariableMap.containsKey(constValue)) { + this.constantVariableMap.put(constValue, new DefinitionSet()); + } + + this.constantVariableMap.get(constValue).add(variable); + } + } + if (!speculative) { previousInstr = opRAVInstr; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index b1910c4bc5ba..325c1a30c6ac 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -42,13 +42,15 @@ public final class RegisterAllocationVerifier { protected RegisterAllocationConfig registerAllocationConfig; - public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution, RegisterAllocationConfig registerAllocationConfig) { + protected Map constantVariableMap; + + public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution, RegisterAllocationConfig registerAllocationConfig, Map constantVariableMap) { this.lir = lir; var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); - this.blockEntryStates.put(cfg.getStartBlock(), new MergedBlockVerifierState(registerAllocationConfig, phiResolution)); + this.blockEntryStates.put(cfg.getStartBlock(), new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantVariableMap)); this.blockStates = new BlockMap<>(cfg); this.phiResolution = phiResolution; @@ -60,6 +62,8 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.fromUsageResolver = new FromUsageResolver(lir, blockInstructions); this.registerAllocationConfig = registerAllocationConfig; + + this.constantVariableMap = constantVariableMap; } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -529,7 +533,7 @@ public void calculateEntryBlocks() { } // Create new entry state for successor blocks out of current block state - var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), registerAllocationConfig, phiResolution); + var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), registerAllocationConfig, phiResolution, this.constantVariableMap); for (var instr : instructions) { state.update(instr, block); } @@ -548,7 +552,7 @@ public void calculateEntryBlocks() { MergedBlockVerifierState succState; if (this.blockEntryStates.get(succ) == null) { - succState = new MergedBlockVerifierState(registerAllocationConfig, phiResolution); + succState = new MergedBlockVerifierState(registerAllocationConfig, phiResolution, this.constantVariableMap); // Either there's no state because it was not yet processed first part of the condition // or, we need to reset it because label changed, second part of the condition From 2ee75cf8b4ee78ca5341375dd116b918c4b9c99b Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 21:18:55 +0100 Subject: [PATCH 026/112] Create a materialization conflict resolver --- .../lir/alloc/verifier/ConflictResolver.java | 13 +++ ...nstantMaterializationConflictResolver.java | 95 +++++++++++++++++++ .../verifier/MergedBlockVerifierState.java | 57 +++-------- .../verifier/PreRegisterAllocationPhase.java | 18 ---- .../verifier/RegisterAllocationVerifier.java | 16 ++-- 5 files changed, 130 insertions(+), 69 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java new file mode 100644 index 000000000000..f156e8121080 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -0,0 +1,13 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.Variable; + +import java.util.List; + +public interface ConflictResolver { + void prepare(LIR lir, BlockMap> blockInstructions); + ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState); + ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState); +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java new file mode 100644 index 000000000000..535e1b5fe732 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -0,0 +1,95 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ConstantMaterializationConflictResolver implements ConflictResolver { + protected Map constantVariableMap; + + public ConstantMaterializationConflictResolver() { + this.constantVariableMap = new HashMap<>(); + } + + @Override + public void prepare(LIR lir, BlockMap> blockInstructions) { + for (var blockId : lir.getBlocks()) { + var instructions = blockInstructions.get(blockId); + + for (var instruction : instructions) { + if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { + var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); + // TODO: loadConstantOp.canRematerializeToStack? + + if (!LIRValueUtil.isVariable(op.dests.orig[0])) { + continue; + } + + var variable = LIRValueUtil.asVariable(op.dests.orig[0]); + var constantValue = new ConstantValue(variable.getValueKind(), loadConstantOp.getConstant()); + + constantVariableMap.put(variable, constantValue); + } + } + } + } + + @Override + public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState) { + var confStates = conflictedState.getConflictedStates(); + + Variable variable = null; + ConstantValue constantValue = null; + + for (var states : confStates) { + var value = states.getValue(); + if (LIRValueUtil.isVariable(value)) { + if (variable != null && !variable.equals(value)) { + return null; + } + + variable = LIRValueUtil.asVariable(value); + } else if (value instanceof ConstantValue constValue) { + if (constantValue != null && !constantValue.equals(constValue)) { + return null; + } + + constantValue = constValue; + } + } + + if (!target.equals(variable) || constantValue == null) { + return null; + } + + if (!this.constantVariableMap.containsKey(variable)) { + return null; + } + + if (!this.constantVariableMap.get(variable).equals(constantValue)) { + return null; + } + + return new ValueAllocationState(variable, null); + } + + @Override + public ValueAllocationState resolveValueState(Variable original, ValueAllocationState valueState) { + if (valueState.getValue() instanceof ConstantValue constant) { + if (!this.constantVariableMap.get(original).equals(constant)) { + return null; + } + + return new ValueAllocationState(original, valueState.source); + } + + return null; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 58ae800ccfa7..42139870c8fa 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -8,32 +8,29 @@ import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; -import java.util.Map; - public class MergedBlockVerifierState { public MergedAllocationStateMap values; protected PhiResolution phiResolution; protected RegisterAllocationConfig registerAllocationConfig; - protected Map constantVariableMap; + protected ConflictResolver conflictConstantResolver; - public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, Map constantVariableMap) { + public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver) { this.values = new MergedAllocationStateMap(); this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; - this.constantVariableMap = constantVariableMap; + this.conflictConstantResolver = constantConflictResolver; } - protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, Map constantVariableMap) { + protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver) { this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; - this.constantVariableMap = constantVariableMap; + this.conflictConstantResolver = constantConflictResolver; if (other == null) { this.values = new MergedAllocationStateMap(); @@ -87,39 +84,9 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } if (state.isConflicted()) { - var conflictedState = (ConflictedAllocationState) state; - - var confStates = conflictedState.getConflictedStates(); - - Variable variable = null; - ConstantValue constantValue = null; - - for (var states : confStates) { - var value = states.getValue(); - if (LIRValueUtil.isVariable(value)) { - if (variable != null && !variable.equals(value)) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); - } - - variable = LIRValueUtil.asVariable(value); - } else if (value instanceof ConstantValue constValue) { - if (constantValue != null && !constantValue.equals(constValue)) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); - } - - constantValue = constValue; - } - } - - if (variable == null || constantValue == null) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); - } - - if (!this.constantVariableMap.containsKey(constantValue)) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); - } - - if (!this.constantVariableMap.get(constantValue).contains(variable)) { + var variable = LIRValueUtil.asVariable(orig); + var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state); + if (resolvedState == null) { throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } @@ -143,12 +110,14 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. continue; } - if (valAllocState.value instanceof ConstantValue constValue) { - if (!this.constantVariableMap.get(constValue).contains(orig)) { + if (valAllocState.value instanceof ConstantValue) { + var variable = LIRValueUtil.asVariable(orig); + var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState); + if (resolvedState == null) { throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } - this.values.put(curr, new ValueAllocationState(orig, op.lirInstruction)); + this.values.put(curr, new ValueAllocationState(variable, op.lirInstruction)); continue; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index fd08ce2bc7f6..c1d4b0a3c58b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -37,14 +37,12 @@ public class PreRegisterAllocationPhase extends AllocationPhase { protected PhiResolution phiResolution; protected TaggedConstantFactory taggedConstantFactory; protected boolean moveConstants; - public Map constantVariableMap; protected PreRegisterAllocationPhase(PhiResolution phiResolution, boolean moveConstants) { this.preallocMap = new HashMap<>(); this.phiResolution = phiResolution; this.taggedConstantFactory = new TaggedConstantFactory(); this.moveConstants = moveConstants; - this.constantVariableMap = new HashMap<>(); } public static class ConstantOverrideValueProcedure implements ValueProcedure { @@ -172,22 +170,6 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo this.preallocMap.put(instruction, opRAVInstr); - if (instruction.isLoadConstantOp()) { - var constOp = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); - var input = constOp.getConstant(); - var output = constOp.getResult(); - - if (LIRValueUtil.isVariable(output)) { - var variable = LIRValueUtil.asVariable(output); - var constValue = new ConstantValue(variable.getValueKind(), input); - if (!this.constantVariableMap.containsKey(constValue)) { - this.constantVariableMap.put(constValue, new DefinitionSet()); - } - - this.constantVariableMap.get(constValue).add(variable); - } - } - if (!speculative) { previousInstr = opRAVInstr; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 325c1a30c6ac..8ec3de6e06d2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -42,15 +42,17 @@ public final class RegisterAllocationVerifier { protected RegisterAllocationConfig registerAllocationConfig; - protected Map constantVariableMap; + protected ConflictResolver constantMaterializationConflictResolver; - public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution, RegisterAllocationConfig registerAllocationConfig, Map constantVariableMap) { + public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; + this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); + var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); - this.blockEntryStates.put(cfg.getStartBlock(), new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantVariableMap)); + this.blockEntryStates.put(cfg.getStartBlock(), new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantMaterializationConflictResolver)); this.blockStates = new BlockMap<>(cfg); this.phiResolution = phiResolution; @@ -62,8 +64,6 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.fromUsageResolver = new FromUsageResolver(lir, blockInstructions); this.registerAllocationConfig = registerAllocationConfig; - - this.constantVariableMap = constantVariableMap; } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -533,7 +533,7 @@ public void calculateEntryBlocks() { } // Create new entry state for successor blocks out of current block state - var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), registerAllocationConfig, phiResolution, this.constantVariableMap); + var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), registerAllocationConfig, phiResolution, constantMaterializationConflictResolver); for (var instr : instructions) { state.update(instr, block); } @@ -552,7 +552,7 @@ public void calculateEntryBlocks() { MergedBlockVerifierState succState; if (this.blockEntryStates.get(succ) == null) { - succState = new MergedBlockVerifierState(registerAllocationConfig, phiResolution, this.constantVariableMap); + succState = new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantMaterializationConflictResolver); // Either there's no state because it was not yet processed first part of the condition // or, we need to reset it because label changed, second part of the condition @@ -672,6 +672,8 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { } public void run() { + this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); + if (this.phiResolution == PhiResolution.FromUsage) { this.fromUsageResolver.resolvePhiFromUsage(); } From e3f33b2a1ae6c4a3ac1472ade2fa90a465673c34 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 19 Jan 2026 22:22:31 +0100 Subject: [PATCH 027/112] Resolve from usage globally --- .../lir/alloc/verifier/DefinitionSet.java | 10 + .../verifier/FromUsageResolverGlobal.java | 316 ++++++++++++++++++ .../verifier/RegisterAllocationVerifier.java | 4 +- 3 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java index 5eff73cff012..76cde443f8ff 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java @@ -1,5 +1,6 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.lir.LIRValueUtil; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; @@ -17,6 +18,11 @@ public DefinitionSet() { this.valueMap = new HashMap<>(); } + public DefinitionSet(DefinitionSet defSet) { + this.internalSet = new HashSet<>(defSet.internalSet); + this.valueMap = new HashMap<>(defSet.valueMap); + } + public void add(Value value) { if (Value.ILLEGAL.equals(value)) { return; @@ -37,6 +43,10 @@ protected String getValueKeyString(Value value) { return regValue.getRegister().toString(); } + if (LIRValueUtil.isVariable(value)) { + return "v" + LIRValueUtil.asVariable(value).index; + } + return value.toString(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java new file mode 100644 index 000000000000..28e2c4ddcd75 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -0,0 +1,316 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.meta.Value; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +/** + * Resolve label variable locations based on their first usage, + * globally - spanning over all program blocks for every variable. + */ +class FromUsageResolverGlobal { + protected LIR lir; + protected BlockMap> blockInstructions; + + public Map> instrToBlockMap; + public Map labelMap; + public Map defined; + public Map reached; + public Map> firstUsages; + public Map initialLocations; + public Map aliasMap; + public BlockMap blockUsageMap; + public List> endBlocks; + + class BlockUsage { + private final DefinitionSet reached; + private final Map locations; + + private BlockUsage() { + this.reached = new DefinitionSet(); + this.locations = new HashMap<>(); + } + + private BlockUsage(BlockUsage blockDefs) { + this.reached = new DefinitionSet(blockDefs.reached); + this.locations = new HashMap<>(blockDefs.locations); + } + + private BlockUsage merge(BlockUsage other) { + var newDefs = new BlockUsage(this); + for (var defString : other.reached.internalSet) { + var defValue = other.reached.valueMap.get(defString); + newDefs.reached.add(defValue); + } + + var iterator = other.locations.keySet().iterator(); + while (iterator.hasNext()) { + var variable = LIRValueUtil.asVariable(iterator.next()); + var defValue = other.locations.get(variable); + newDefs.locations.put(variable, defValue); + } + + return newDefs; + } + } + + protected FromUsageResolverGlobal(LIR lir, BlockMap> blockInstructions) { + this.lir = lir; + this.blockInstructions = blockInstructions; + + this.instrToBlockMap = new HashMap<>(); + this.labelMap = new HashMap<>(); + this.defined = new HashMap<>(); + this.reached = new HashMap<>(); + this.firstUsages = new HashMap<>(); + this.initialLocations = new HashMap<>(); + this.aliasMap = new HashMap<>(); + this.endBlocks = new ArrayList<>(); + + this.blockUsageMap = new BlockMap<>(lir.getControlFlowGraph()); + } + + /** + * Resolves label variable registers by finding where they are used. + */ + public void resolvePhiFromUsage() { + Queue> worklist = new LinkedList<>(); + + this.initializeUsages(); + + for (var block : endBlocks) { + blockUsageMap.put(block, new BlockUsage()); + worklist.add(block); + } + + while (!worklist.isEmpty()) { + var block = worklist.remove(); + + var usage = blockUsageMap.get(block); + var instructions = blockInstructions.get(block); + for (var instruction : instructions.reversed()) { + switch (instruction) { + case RAVInstruction.Move move -> handleMove(usage, move.from, move.to); + case RAVInstruction.Spill spill -> handleMove(usage, spill.from, spill.to); + case RAVInstruction.Reload reload -> handleMove(usage, reload.from, reload.to); + case RAVInstruction.StackMove stackMove -> handleMove(usage, stackMove.from, stackMove.to); + case RAVInstruction.Op op -> { + if (op.lirInstruction instanceof StandardOp.LabelOp) { + for (int i = 0; i < op.dests.count; i++) { + if (!LIRValueUtil.isVariable(op.dests.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(op.dests.orig[i]); + if (usage.locations.get(variable) == null) { + continue; + } + + if (op.dests.curr[i] != null) { + continue; + } + + resolveVariable(usage, variable); + usage.locations.put(variable, null); + } + + continue; + } + + if (firstUsages.containsKey(op)) { + var iterator = firstUsages.get(op).iterator(); + while (iterator.hasNext()) { + var variable = LIRValueUtil.asVariable(iterator.next()); + usage.locations.put(variable, initialLocations.get(variable)); + usage.reached.add(variable); + } + } + } + default -> { + } + } + } + + for (int i = 0; i < block.getPredecessorCount(); i++) { + var pred = block.getPredecessorAt(i); + + if (this.blockUsageMap.get(pred) == null) { + this.blockUsageMap.put(pred, new BlockUsage(usage)); + } else { + var predReached = this.blockUsageMap.get(pred); + var newReached = predReached.merge(usage); + + this.blockUsageMap.put(pred, newReached); + if (predReached.reached.internalSet.size() == newReached.reached.internalSet.size()) { + continue; + } + } + + worklist.remove(pred); + worklist.add(pred); + } + } + } + + protected void initializeUsages() { + Queue> worklist = new LinkedList<>(); + + var startBlock = this.lir.getControlFlowGraph().getStartBlock(); + worklist.add(startBlock); + + // Calculate what is defined when + usages + Set> visited = new HashSet<>(); + while (!worklist.isEmpty()) { + var block = worklist.remove(); + if (visited.contains(block)) { + continue; + } + + visited.add(block); + + var instructions = blockInstructions.get(block); + var label = (RAVInstruction.Op) instructions.getFirst(); + + instrToBlockMap.put(label, block); + + for (var i = 0; i < label.dests.count; i++) { + if (LIRValueUtil.isVariable(label.dests.orig[i])) { + var variable = LIRValueUtil.asVariable(label.dests.orig[i]); + defined.put(variable, true); + labelMap.put(variable, label); + } + } + + for (var instruction : instructions) { + if (instruction instanceof RAVInstruction.Op op) { + if (op.lirInstruction instanceof StandardOp.JumpOp) { + continue; + } + + handleUsages(op.uses, op, block); + handleUsages(op.alive, op, block); + } + } + + var jump = (RAVInstruction.Op) instructions.getLast(); + for (var i = 0; i < jump.alive.count; i++) { + if (!LIRValueUtil.isVariable(jump.alive.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(jump.alive.orig[i]); + if (defined.containsKey(variable) && !reached.containsKey(variable)) { + // No usage found before this jump + var succ = block.getSuccessorAt(0); + var succLabel = (RAVInstruction.Op) blockInstructions.get(succ).getFirst(); + + aliasMap.put(variable, LIRValueUtil.asVariable(succLabel.dests.orig[i])); + } + } + + if (block.getSuccessorCount() == 0) { + endBlocks.add(block); + continue; + } + + for (int i = 0; i < block.getSuccessorCount(); i++) { + var succ = block.getSuccessorAt(i); + + worklist.remove(succ); + worklist.add(succ); + } + } + } + + protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { + for (var i = 0; i < values.count; i++) { + if (!LIRValueUtil.isVariable(values.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(values.orig[i]); + if (defined.containsKey(variable) && !reached.containsKey(variable)) { + // Defined - variable comes from label + // Reached does not contain variable - there's no other first usage. + reached.put(variable, false); + + if (!firstUsages.containsKey(op)) { + firstUsages.put(op, new HashSet<>()); + } + + firstUsages.get(op).add(variable); + instrToBlockMap.put(op, block); + initialLocations.put(variable, values.curr[i]); + + aliasMap.remove(variable); + } + } + } + + protected void handleMove(BlockUsage usage, Value from, Value to) { + for (var entry : usage.locations.entrySet()) { + var variable = LIRValueUtil.asVariable(entry.getKey()); + var location = entry.getValue(); + if (location == null) { + continue; + } + + if (location.equals(to) && usage.reached.contains(variable)) { + usage.locations.put(variable, from); + } + } + } + + protected void resolveVariable(BlockUsage usage, Variable variable) { + var label = labelMap.get(variable); + var block = instrToBlockMap.get(label); + var location = usage.locations.get(variable); + + for (int i = 0; i < label.dests.count; i++) { + if (!LIRValueUtil.isVariable(label.dests.orig[i])) { + continue; + } + + var labelVar = LIRValueUtil.asVariable(label.dests.orig[i]); + if (!labelVar.equals(variable)) { + continue; + } + + label.dests.curr[i] = location; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + + jump.alive.curr[i] = location; + } + + break; + } + + // Variables that are passed into jumps without any other usage are aliases + // for same variable in successor label, whenever said variable is resolved + // we now have a location for this variable and can take other moves into account. + for (var entry : aliasMap.entrySet()) { + var aliased = LIRValueUtil.asVariable(entry.getValue()); + if (variable.equals(aliased)) { + var alias = LIRValueUtil.asVariable(entry.getKey()); + + usage.locations.put(alias, location); + usage.reached.add(alias); + } + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 8ec3de6e06d2..cacb2e34f5b7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -38,7 +38,7 @@ public final class RegisterAllocationVerifier { // FromPredecessors resolver public BlockMap blockDefinitions; - public FromUsageResolver fromUsageResolver; + public FromUsageResolverGlobal fromUsageResolver; protected RegisterAllocationConfig registerAllocationConfig; @@ -62,7 +62,7 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.variableRegisterMap = new HashMap<>(); this.usageAliasMap = new HashMap<>(); - this.fromUsageResolver = new FromUsageResolver(lir, blockInstructions); + this.fromUsageResolver = new FromUsageResolverGlobal(lir, blockInstructions); this.registerAllocationConfig = registerAllocationConfig; } From 569612dcf937fabeafb390b7c13c74d746973a9d Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 21 Jan 2026 11:04:54 +0100 Subject: [PATCH 028/112] Extract values from FrameState and verify its contents --- .../verifier/MergedBlockVerifierState.java | 34 +++++++++++++++++++ .../verifier/PreRegisterAllocationPhase.java | 22 ++++++++---- .../lir/alloc/verifier/RAVInstruction.java | 8 +++++ 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 42139870c8fa..852428f129a3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -10,6 +10,7 @@ import jdk.graal.compiler.lir.StandardOp; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Value; public class MergedBlockVerifierState { @@ -186,6 +187,39 @@ public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstr } } + this.checkInputs(op.stateValues, op, block, labelOp); + + int kindIdx = 0; + for (int i = 0; i < op.stateValues.count; i++) { + var orig = op.stateValues.orig[i]; + var curr = op.stateValues.curr[i]; + + var state = this.values.get(curr); + if (state instanceof ValueAllocationState valueAllocationState && valueAllocationState.getValue().equals(orig)) { + continue; + } + + JavaKind kind = null; + while (kindIdx < op.kinds.length) { + JavaKind target = op.kinds[kindIdx++]; + if (!JavaKind.Illegal.equals(target)) { + kind = target; + break; + } + // Illegal values are ignored when iterating over state values + // but kept in the kinds array so we need to skip them. + } + + if (!JavaKind.Object.equals(kind)) { + continue; + } + + // TODO: how to handle object kind? + // If the same virtual value is present in the register then there isn't anything to be done + // but maybe if it was changed we also maybe want to make sure that there's a pointer (Object) present? + // but for that we would need to track that information based on GC instructions + } + this.checkAliveConstraint(op, block); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index c1d4b0a3c58b..9cd6667e8448 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -4,6 +4,7 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.InstructionStateProcedure; +import jdk.graal.compiler.lir.InstructionValueConsumer; import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRFrameState; @@ -167,6 +168,20 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); + instruction.forEachState(opRAVInstr.stateValues.copyOriginalProc); + instruction.forEachState(new InstructionStateProcedure() { + @Override + public void doState(LIRInstruction instruction, LIRFrameState state) { + if (state.topFrame == null) { + return; + } + + // Haven't found a case where there is multiple frame states on an instruction + // so this will work, otherwise appending them would do the job in that case + // if we could also get this information about VirtualObjects. + opRAVInstr.kinds = state.topFrame.getSlotKinds(); + } + }); this.preallocMap.put(instruction, opRAVInstr); @@ -226,12 +241,7 @@ public BlockMap> getVerifierInstructions(LIR lir) { instruction.forEachOutput(opRAVInstr.dests.copyCurrentProc); instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); - instruction.forEachState(new InstructionStateProcedure() { - @Override - public void doState(LIRInstruction instruction, LIRFrameState state) { - - } - }); + instruction.forEachState(opRAVInstr.stateValues.copyCurrentProc); instructionList.add(opRAVInstr); var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index d0a7fe8a9514..9d013386fcc9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -6,8 +6,10 @@ import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Value; +import java.util.ArrayList; import java.util.EnumSet; import java.util.LinkedList; import java.util.List; @@ -159,6 +161,9 @@ public static class Op extends Base { public ValueArrayPair temp; public ValueArrayPair alive; + public JavaKind[] kinds; + public ValueArrayPair stateValues; + public Op(LIRInstruction instruction) { super(instruction); @@ -175,6 +180,9 @@ public Op(LIRInstruction instruction) { instruction.forEachAlive(countValuesProc); this.alive = new ValueArrayPair(countValuesProc.getCount()); + + instruction.forEachState(countValuesProc); + this.stateValues = new ValueArrayPair(countValuesProc.getCount()); } public boolean hasMissingDefinitions() { From 00aa04a57010305b8bf4ac78d6bac66deac4dd69 Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 30 Jan 2026 17:16:04 +0100 Subject: [PATCH 029/112] Fix typo in ValueNotInRegisterException --- .../lir/alloc/verifier/ValueNotInRegisterException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 74b56fc974db..b76cc0888c91 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -29,7 +29,7 @@ static String getErrorMessage(LIRInstruction instruction, BasicBlock block, V .append(variable) .append(" not found in ") .append(location) - .append("for instruction ") + .append(" for instruction ") .append(instruction) .append(" in ") .append(block) From 2ae6469007114fb6383382c2f6af5ae51825f52a Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 30 Jan 2026 17:35:03 +0100 Subject: [PATCH 030/112] Make sure variable can rematerialze as constant --- ...nstantMaterializationConflictResolver.java | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 535e1b5fe732..7e4b4085a300 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -1,5 +1,6 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; @@ -21,23 +22,28 @@ public ConstantMaterializationConflictResolver() { @Override public void prepare(LIR lir, BlockMap> blockInstructions) { for (var blockId : lir.getBlocks()) { - var instructions = blockInstructions.get(blockId); + var block = lir.getBlockById(blockId); + var instructions = blockInstructions.get(block); for (var instruction : instructions) { - if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { - var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); - // TODO: loadConstantOp.canRematerializeToStack? - - if (!LIRValueUtil.isVariable(op.dests.orig[0])) { - continue; - } + this.prepareFromInstr(instruction, block); + } + } + } - var variable = LIRValueUtil.asVariable(op.dests.orig[0]); - var constantValue = new ConstantValue(variable.getValueKind(), loadConstantOp.getConstant()); + public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { + if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { + var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); + // TODO: loadConstantOp.canRematerializeToStack? - constantVariableMap.put(variable, constantValue); - } + if (!LIRValueUtil.isVariable(op.dests.orig[0])) { + return; } + + var variable = LIRValueUtil.asVariable(op.dests.orig[0]); + var constantValue = new ConstantValue(variable.getValueKind(), loadConstantOp.getConstant()); + + constantVariableMap.put(variable, constantValue); } } @@ -82,6 +88,10 @@ public ValueAllocationState resolveConflictedState(Variable target, ConflictedAl @Override public ValueAllocationState resolveValueState(Variable original, ValueAllocationState valueState) { + if (!this.constantVariableMap.containsKey(original)) { + return null; + } + if (valueState.getValue() instanceof ConstantValue constant) { if (!this.constantVariableMap.get(original).equals(constant)) { return null; From 4c7cc15a1f7a2df891bc8fd4871dcdb26c24009d Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 30 Jan 2026 20:36:25 +0100 Subject: [PATCH 031/112] Add shared state for verifier phases --- .../verifier/PreRegisterAllocationPhase.java | 139 ++---------- .../RegisterAllocationVerifierPhase.java | 203 ++++++++++++++---- .../RegisterAllocationVerifierPhaseState.java | 41 ++++ .../compiler/lir/phases/AllocationStage.java | 11 +- 4 files changed, 225 insertions(+), 169 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 9cd6667e8448..5d0a403ae175 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -1,10 +1,8 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.InstructionStateProcedure; -import jdk.graal.compiler.lir.InstructionValueConsumer; import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRFrameState; @@ -13,7 +11,6 @@ import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.ValueProcedure; import jdk.graal.compiler.lir.Variable; -import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.gen.LIRGenerator; import jdk.graal.compiler.lir.phases.AllocationPhase; @@ -27,23 +24,17 @@ import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; public class PreRegisterAllocationPhase extends AllocationPhase { - protected Map preallocMap; - protected PhiResolution phiResolution; protected TaggedConstantFactory taggedConstantFactory; - protected boolean moveConstants; + protected RegisterAllocationVerifierPhaseState state; - protected PreRegisterAllocationPhase(PhiResolution phiResolution, boolean moveConstants) { - this.preallocMap = new HashMap<>(); - this.phiResolution = phiResolution; + public PreRegisterAllocationPhase(RegisterAllocationVerifierPhaseState state) { this.taggedConstantFactory = new TaggedConstantFactory(); - this.moveConstants = moveConstants; + this.state = state; } public static class ConstantOverrideValueProcedure implements ValueProcedure { @@ -100,14 +91,15 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope } }; - public Map getPreallocMap() { - return preallocMap; - } - @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - LIR lir = lirGenRes.getLIR(); + var compUnitName = lirGenRes.getCompilationUnitName(); + if (state.filterStr != null && !compUnitName.contains(state.filterStr)) { + return; + } + var preallocMap = state.createInstructionMap(lirGenRes); + LIR lir = lirGenRes.getLIR(); Map constantValueMap = new HashMap<>(); var constOverwriteProc = new ConstantOverrideValueProcedure(lir, constantValueMap); @@ -120,8 +112,8 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo RAVInstruction.Base previousInstr = null; for (var instruction : instructions) { - if (instruction instanceof StandardOp.JumpOp && this.phiResolution == PhiResolution.FromJump) { - if (this.moveConstants) { + if (instruction instanceof StandardOp.JumpOp && this.state.phiResolution == PhiResolution.FromJump) { + if (this.state.moveConstants) { instruction.forEachAlive(constOverwriteProc); } else { instruction.forEachAlive(taggedConstantValueProc); @@ -183,7 +175,7 @@ public void doState(LIRInstruction instruction, LIRFrameState state) { } }); - this.preallocMap.put(instruction, opRAVInstr); + preallocMap.put(instruction, opRAVInstr); if (!speculative) { previousInstr = opRAVInstr; @@ -201,7 +193,7 @@ public void doState(LIRInstruction instruction, LIRFrameState state) { var mov = context.spillMoveFactory.createMove(v, constantValueMap.get(v)); var ravInstr = new RAVInstruction.Op(mov); mov.forEachOutput(ravInstr.dests.copyOriginalProc); - this.preallocMap.put(mov, ravInstr); + preallocMap.put(mov, ravInstr); instructions.add(mov); } @@ -209,111 +201,6 @@ public void doState(LIRInstruction instruction, LIRFrameState state) { } } - public BlockMap> getVerifierInstructions(LIR lir) { - Set presentInstructions = new HashSet<>(); - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - ArrayList instructions = lir.getLIRforBlock(block); - presentInstructions.addAll(instructions); - } - - BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - var instructionList = new LinkedList(); - - ArrayList instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - var rAVInstr = preallocMap.get(instruction); - if (rAVInstr == null) { - var movOp = this.getRAVMoveInstruction(instruction); - if (movOp != null) { - instructionList.add(movOp); - continue; - } - - throw new UnknownInstructionError(instruction, block); - } - - var opRAVInstr = (RAVInstruction.Op) rAVInstr; - - instruction.forEachInput(opRAVInstr.uses.copyCurrentProc); - instruction.forEachOutput(opRAVInstr.dests.copyCurrentProc); - instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); - instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); - instruction.forEachState(opRAVInstr.stateValues.copyCurrentProc); - - instructionList.add(opRAVInstr); - var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); - - if (!speculativeMoves.isEmpty()) { - for (var speculativeMove : speculativeMoves) { - if (!presentInstructions.contains(speculativeMove.getLIRInstruction())) { - instructionList.add(speculativeMove); - } - } - } - - var virtualMoves = opRAVInstr.getVirtualMoveList(); - instructionList.addAll(virtualMoves); - } - - blockInstructions.put(block, instructionList); - } - return blockInstructions; - } - - /** - * Create Register Verifier Instruction that was created by the Register Allocator. - * Generally speaking, it's always a move instruction, other ones return null. - * - * @param instruction LIRInstruction newly created by Register Allocator - * @return Spill, Reload, Move or null if instruction is not a move - */ - protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - if (instruction.isLoadConstantOp()) { - var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); - var constant = constatLoad.getConstant(); - var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot - - // This isn't really a virtual move, but it currently acts the same, so we keep it, - // we take constants as variables. TODO: maybe remove virtual move altogether for Move(reg, var/constant) - return new RAVInstruction.VirtualMove(instruction, new ConstantValue(result.getValueKind(), constant), result); - } - - return null; - } - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - - var input = valueMov.getInput(); - var result = valueMov.getResult(); - - if (input instanceof VirtualStackSlot stackSlot && result instanceof RegisterValue reg) { - return new RAVInstruction.Reload(instruction, reg, stackSlot); - } else if (result instanceof VirtualStackSlot stackSlot && input instanceof RegisterValue reg) { - return new RAVInstruction.Spill(instruction, stackSlot, reg); - } else if (input instanceof RegisterValue reg1 && result instanceof RegisterValue reg2) { - return new RAVInstruction.Move(instruction, reg1, reg2); - } else if (input instanceof StackSlot stackSlot && result instanceof RegisterValue reg) { - return new RAVInstruction.Reload(instruction, reg, stackSlot); - } else if (input instanceof RegisterValue reg && result instanceof StackSlot stackSlot) { - return new RAVInstruction.Spill(instruction, stackSlot, reg); - } - - if (input instanceof StackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof StackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } - - return null; - } - /** * Determines if instruction is a virtual move, a virtual move is * a move instruction that moves a real register value into a variable, diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index d2be186d3dfb..2d69eae01d24 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -1,14 +1,31 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.AllocationPhase; import jdk.graal.compiler.options.EnumOptionKey; 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.RegisterValue; +import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + public class RegisterAllocationVerifierPhase extends AllocationPhase { public static class Options { @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) @@ -19,60 +36,166 @@ public static class Options { @Option(help = "Should constants be moved to variables when needed", type = OptionType.Debug) public static final OptionKey MoveConstants = new OptionKey<>(true); + + @Option(help = "Substring necessary to be found for method to be verified", type = OptionType.Debug) + public static final OptionKey RAFilter = new OptionKey<>(null); } - public static String[] ignoredTestCases = { - // Disable Truffle Related Tests - // New instructions being added makes the verifier not work, investigate why prealloc is not run. - "truffle", "Truffle", "polyglot", "Polyglot", "Root[]", "InstrumentationTestLanguage", "NFITest", "intCaller1", "callee"}; + private RegisterAllocationVerifierPhaseState state; - public static boolean isIgnored(String compUnitName) { - for (String ignoredTest : ignoredTestCases) { - if (compUnitName.contains(ignoredTest)) { - return true; - } - } - return false; + public RegisterAllocationVerifierPhase(RegisterAllocationVerifierPhaseState state) { + this.state = state; } - private PhiResolution phiResolution; - private boolean moveConstants; + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + var compUnitName = lirGenRes.getCompilationUnitName(); + if (state.filterStr != null && !compUnitName.contains(state.filterStr)) { + // Filter for compilation unit substring to run verification only on + // certain methods, cannot use MethodFilter here because I cannot + // access JavaMethod here. + return; + } + + var instructions = getVerifierInstructions(lirGenRes); + var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.state.phiResolution, context.registerAllocationConfig); - public RegisterAllocationVerifierPhase(OptionValues options) { - this.phiResolution = Options.RAPhiResolution.getValue(options); - this.moveConstants = Options.MoveConstants.getValue(options); + verifier.run(); } - protected PreRegisterAllocationPhase preallocPhaseRAVerifier; + protected BlockMap> getVerifierInstructions(LIRGenerationResult lirGenRes) { + var lir = lirGenRes.getLIR(); + var preallocMap = state.getInstructionMap(lirGenRes); + + Map definedVariables = new HashMap<>(); + Set presentInstructions = new HashSet<>(); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + ArrayList instructions = lir.getLIRforBlock(block); + + for (var instruction : instructions) { + presentInstructions.add(instruction); + + var rAVInstr = preallocMap.get(instruction); + if (rAVInstr instanceof RAVInstruction.Op op) { + for (int i = 0; i < op.dests.count; i++) { + if (LIRValueUtil.isVariable(op.dests.orig[i])) { + var variable = LIRValueUtil.asVariable(op.dests.orig[i]); + definedVariables.put(variable, op); + } + } + } + } + } + + BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + var instructionList = new LinkedList(); + + ArrayList instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + var rAVInstr = preallocMap.get(instruction); + if (rAVInstr == null) { + var movOp = this.getRAVMoveInstruction(instruction); + if (movOp != null) { + instructionList.add(movOp); + continue; + } + + // TestCase: TruffleHotSpotCompilation-75393[TruffleSafepointTest.TestRootNode@f25a8e].cfg + throw new UnknownInstructionError(instruction, block); + } + + var opRAVInstr = (RAVInstruction.Op) rAVInstr; - public AllocationPhase getPreAllocPhase() { - this.preallocPhaseRAVerifier = new PreRegisterAllocationPhase(phiResolution, moveConstants); - return this.preallocPhaseRAVerifier; + instruction.forEachInput(opRAVInstr.uses.copyCurrentProc); + instruction.forEachOutput(opRAVInstr.dests.copyCurrentProc); + instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); + instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); + instruction.forEachState(opRAVInstr.stateValues.copyCurrentProc); + + instructionList.add(opRAVInstr); + var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); + + if (!speculativeMoves.isEmpty()) { + for (var speculativeMove : speculativeMoves) { + if (presentInstructions.contains(speculativeMove.getLIRInstruction())) { + continue; + } + + if (!LIRValueUtil.isVariable(speculativeMove.location) && LIRValueUtil.isVariable(speculativeMove.variableOrConstant)) { + var variable = LIRValueUtil.asVariable(speculativeMove.variableOrConstant); + if (definedVariables.containsKey(variable)) { + continue; + } + } + + instructionList.add(speculativeMove); + } + } + + var virtualMoves = opRAVInstr.getVirtualMoveList(); + instructionList.addAll(virtualMoves); + + preallocMap.remove(instruction); + } + + blockInstructions.put(block, instructionList); + } + + state.deleteInstructionMap(lirGenRes); + return blockInstructions; } - @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - var compUnitName = lirGenRes.getCompilationUnitName(); - // if (!compUnitName.contains("DirectByteBufferTest.unalignedWriteSnippet")) { - // return; - // } - - if (RegisterAllocationVerifierPhase.isIgnored(compUnitName)) { - // Skipping truffle test cases because they cause trouble with - // prealloc and getVerifierInstructions, because blocks and - // instructions that aren't made only by the RA are added. - return; + /** + * Create Register Verifier Instruction that was created by the Register Allocator. + * Generally speaking, it's always a move instruction, other ones return null. + * + * @param instruction LIRInstruction newly created by Register Allocator + * @return Spill, Reload, Move or null if instruction is not a move + */ + protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + if (instruction.isLoadConstantOp()) { + var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); + var constant = constatLoad.getConstant(); + var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot + + // This isn't really a virtual move, but it currently acts the same, so we keep it, + // we take constants as variables. TODO: maybe remove virtual move altogether for Move(reg, var/constant) + return new RAVInstruction.VirtualMove(instruction, new ConstantValue(result.getValueKind(), constant), result); + } + + return null; } + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - assert this.preallocPhaseRAVerifier != null : "Phase before register allocation was not run, cannot verify it."; + var input = valueMov.getInput(); + var result = valueMov.getResult(); - var instructions = this.preallocPhaseRAVerifier.getVerifierInstructions(lirGenRes.getLIR()); - var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.phiResolution, context.registerAllocationConfig); + if (input instanceof VirtualStackSlot stackSlot && result instanceof RegisterValue reg) { + return new RAVInstruction.Reload(instruction, reg, stackSlot); + } else if (result instanceof VirtualStackSlot stackSlot && input instanceof RegisterValue reg) { + return new RAVInstruction.Spill(instruction, stackSlot, reg); + } else if (input instanceof RegisterValue reg1 && result instanceof RegisterValue reg2) { + return new RAVInstruction.Move(instruction, reg1, reg2); + } else if (input instanceof StackSlot stackSlot && result instanceof RegisterValue reg) { + return new RAVInstruction.Reload(instruction, reg, stackSlot); + } else if (input instanceof RegisterValue reg && result instanceof StackSlot stackSlot) { + return new RAVInstruction.Spill(instruction, stackSlot, reg); + } - try { - verifier.run(); - } catch (RAVException e) { - System.err.println("Verification failed - " + compUnitName); + if (input instanceof StackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof StackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { + return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); } + + return null; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java new file mode 100644 index 000000000000..bf621ef84c10 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -0,0 +1,41 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.gen.LIRGenerationResult; +import jdk.graal.compiler.options.OptionValues; + +import java.util.IdentityHashMap; +import java.util.Map; + +public class RegisterAllocationVerifierPhaseState { + public PhiResolution phiResolution; + public boolean moveConstants; + public String filterStr; + + protected Map> verifierInstructions; + + public RegisterAllocationVerifierPhaseState(OptionValues options) { + this.verifierInstructions = new IdentityHashMap<>(); + + this.phiResolution = RegisterAllocationVerifierPhase.Options.RAPhiResolution.getValue(options); + this.moveConstants = RegisterAllocationVerifierPhase.Options.MoveConstants.getValue(options); + this.filterStr = RegisterAllocationVerifierPhase.Options.RAFilter.getValue(options); + } + + public Map createInstructionMap(LIRGenerationResult lirGenRes) { + this.verifierInstructions.put(lirGenRes, new IdentityHashMap<>()); + return this.verifierInstructions.get(lirGenRes); + } + + public Map getInstructionMap(LIRGenerationResult lirGenRes) { + if (!this.verifierInstructions.containsKey(lirGenRes)) { + throw new IllegalStateException(); + } + + return this.verifierInstructions.get(lirGenRes); + } + + public void deleteInstructionMap(LIRGenerationResult lirGenRes) { + verifierInstructions.remove(lirGenRes); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java index 0ce08ca32c48..5aaa4e8d7fdf 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java @@ -26,7 +26,9 @@ import jdk.graal.compiler.debug.Assertions; import jdk.graal.compiler.lir.alloc.AllocationStageVerifier; +import jdk.graal.compiler.lir.alloc.verifier.PreRegisterAllocationPhase; import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhase; +import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhaseState; import jdk.graal.compiler.lir.stackslotalloc.LSStackSlotAllocator; import jdk.graal.compiler.lir.stackslotalloc.SimpleStackSlotAllocator; import jdk.graal.compiler.lir.alloc.lsra.LinearScanPhase; @@ -42,10 +44,13 @@ public AllocationStage(OptionValues options) { // For now, verify before stack allocator if (RegisterAllocationVerifierPhase.Options.EnableRAVerifier.getValue(options)) { - var raPhase = new RegisterAllocationVerifierPhase(options); + var sharedState = new RegisterAllocationVerifierPhaseState(options); - prependPhase(raPhase.getPreAllocPhase()); - appendPhase(raPhase); + var preAllocRAVPhase = new PreRegisterAllocationPhase(sharedState); + var ravPhase = new RegisterAllocationVerifierPhase(sharedState); + + prependPhase(preAllocRAVPhase); + appendPhase(ravPhase); } // build frame map From fd9d6920490e1eb67a4e9fe49e4d52cbc42fa544 Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 30 Jan 2026 20:44:56 +0100 Subject: [PATCH 032/112] Remove unused memory map --- .../verifier/MergedAllocationStateMap.java | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index 69fbad530d02..0afb1ee6e9a1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -35,39 +35,12 @@ public class MergedAllocationStateMap { protected Map prioritizedLocations; protected int time; - // The idea behind this is that we do not care where the address is going - // we care about if we ever saw this in memory, then we return ValueAllocationState - // CompositeValue AMD64AddressValue&AArch64AddressValue - protected Set inMemoryValues; - - protected class ValueWrapper { - protected Value value; - - protected ValueWrapper(Value value) { - this.value = value; - } - - protected Value getValue() { - return this.value; - } - - @Override - public String toString() { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); - } - - return value.toString(); - } - } - public MergedAllocationStateMap() { valueMap = new HashMap<>(); internalMap = new HashMap<>(); locationTimings = new HashMap<>(); prioritizedLocations = new HashMap<>(); time = 0; - inMemoryValues = new HashSet<>(); } public MergedAllocationStateMap(MergedAllocationStateMap other) { @@ -76,7 +49,6 @@ public MergedAllocationStateMap(MergedAllocationStateMap other) { locationTimings = new HashMap<>(other.locationTimings); prioritizedLocations = new HashMap<>(other.prioritizedLocations); time = other.time + 1; - inMemoryValues = new HashSet<>(other.inMemoryValues); } public AllocationState get(Value key) { From 499c44d6649ec49d10ac2c854081f2df425e22e1 Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 30 Jan 2026 23:02:42 +0100 Subject: [PATCH 033/112] Split resolvers into their own classes --- .../lir/alloc/verifier/FromJumpResolver.java | 96 ++++ .../verifier/FromPredecessorsResolver.java | 335 +++++++++++++ .../verifier/FromUsageResolverGlobal.java | 89 ++-- .../alloc/verifier/LabelConflictResolver.java | 151 ++++++ .../verifier/MergedBlockVerifierState.java | 70 ++- .../lir/alloc/verifier/PhiResolution.java | 5 +- .../verifier/RegisterAllocationVerifier.java | 454 ++---------------- 7 files changed, 710 insertions(+), 490 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java new file mode 100644 index 000000000000..53ba9d953f16 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java @@ -0,0 +1,96 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.vm.ci.meta.Value; + +import java.util.List; + +public class FromJumpResolver { + public LIR lir; + public BlockMap> blockInstructions; + public BlockMap blockStates; + + public FromJumpResolver(LIR lir, BlockMap> blockInstructions, BlockMap blockStates) { + this.lir = lir; + this.blockInstructions = blockInstructions; + this.blockStates = blockStates; + } + + /** + * Resolve phi registers from this jump point, can only be done if + * the phi variables/constants only have one location. + * + *

+ * This way we can handle phi arguments before we reach said block. + * But cannot really be done for loops, if their initial values + * are the same and there are multiple ones. + *

+ * + * @param block From whom we are jumping from + */ + public void resolvePhiFromJump(BasicBlock block) { + var state = this.blockStates.get(block); + var jumpInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getLast(); + + for (int i = 0; i < jumpInstr.alive.count; i++) { + if (jumpInstr.alive.curr[i] != null) { + continue; + } + + var inputValue = jumpInstr.alive.orig[i]; + if (!LIRValueUtil.isVariable(inputValue) && !(inputValue instanceof ConstantValue)) { + continue; + } + + var registerValue = this.getRegisterBeforeJump(state, inputValue); + if (registerValue == null) { + return; + } + + jumpInstr.alive.curr[i] = registerValue; + for (int j = 0; j < block.getSuccessorCount(); j++) { + var succ = block.getSuccessorAt(j); + var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); + + labelInstr.dests.curr[i] = jumpInstr.alive.curr[i]; + + for (int k = 0; k < succ.getPredecessorCount(); k++) { + // Sibling jumps need to be updated as well in order to pass input checks. + var sibling = succ.getPredecessorAt(k); + if (sibling.equals(block)) { + continue; + } + + var siblingJumpInstr = (RAVInstruction.Op) this.blockInstructions.get(sibling).getLast(); + siblingJumpInstr.alive.curr[i] = jumpInstr.alive.curr[i]; + } + } + } + } + + private Value getRegisterBeforeJump(MergedBlockVerifierState state, Value inputValue) { + // Does not work for constants if there is more of them + var locations = state.values.getValueLocations(inputValue); + if (locations.isEmpty()) { + return null; + } + + var register = locations.stream().findFirst().get(); + if (locations.size() != 1) { + int time = -1; + for (var location : locations) { + var locTime = state.values.getKeyTime(location); + if (locTime > time) { + register = location; + time = locTime; + } + } + } + + return register; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java new file mode 100644 index 000000000000..15fac51c9ecc --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -0,0 +1,335 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +public class FromPredecessorsResolver { + public LIR lir; + public BlockMap> blockInstructions; + public BlockMap blockStates; // Current states + public BlockMap blockEntryStates; // State on entry to block + + public FromPredecessorsResolver(LIR lir, BlockMap> blockInstructions, BlockMap blockStates, BlockMap blockEntryStates) { + this.lir = lir; + this.blockInstructions = blockInstructions; + this.blockStates = blockStates; + this.blockEntryStates = blockEntryStates; + } + + /** + * Fill in missing variable locations for current block's label instruction and predecessor + * jump instructions. + *

+ * We are looking for intersection between locations of individual processors, this should + * give us a single register that is used for the phi, and is necessary for jump to verify + * that contents are alive that that point and for label to define where the result of phi + * will be held to used in said block. + * + * @param block Block that needs phi function output + * @param labelInstr Label instruction of said block + */ + public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { + for (int i = 0; i < labelInstr.dests.count; i++) { + Set locations = null; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + var inputValue = jump.alive.orig[i]; + + var varLoc = state.values.getValueLocations(inputValue); + if (locations == null) { + locations = varLoc; + continue; + } + + locations.retainAll(varLoc); + } + + Value location = null; + if (locations.size() != 1) { + if (locations.isEmpty()) { + return false; + } + + for (int j = 0; j < block.getPredecessorCount(); j++) { + int time = -1; + Value blockReg = null; + for (var loc : locations) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + + var regTime = state.values.getKeyTime(loc); + if (regTime > time) { + // TODO: replace time with priority of Moves inserted by the Register Allocator. + time = regTime; // Max time + blockReg = loc; + } + } + + if (location == null) { + location = blockReg; + } else if (!location.equals(blockReg)) { + // Not same for all blocks, so none choosen. + return false; + } + } + } else { + location = locations.stream().findFirst().get(); + } + + var registerValue = location; + // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); + + labelInstr.dests.curr[i] = registerValue; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + jump.alive.curr[i] = registerValue; + } + } + + return true; + } + + class VariableLocations implements Iterable { + protected Set internalList; + protected Map valueMap; + + public VariableLocations() { + this.internalList = new HashSet<>(); + this.valueMap = new HashMap<>(); + } + + public VariableLocations(VariableLocations other) { + this.internalList = new HashSet<>(other.internalList); + this.valueMap = new HashMap<>(other.valueMap); + } + + public void add(Value location) { + var locString = getValueKeyString(location); + internalList.add(locString); + valueMap.put(locString, location); + } + + public void remove(Value location) { + var locString = getValueKeyString(location); + internalList.remove(locString); + valueMap.remove(locString); + } + + public boolean isEmpty() { + return internalList.isEmpty(); + } + + public boolean contains(Value location) { + var locString = getValueKeyString(location); + return valueMap.containsKey(locString); + } + + protected String getValueKeyString(Value value) { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + return value.toString(); + } + + @Override + public Iterator iterator() { + return internalList.stream().map(valueMap::get).iterator(); + } + } + + public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { + var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); + + // Definition block needs to have this set. + var propagateMap = new HashMap, List>(); + var locationMap = new HashMap, Map>(); + + var defVariableToLocations = new HashMap(); + var defBlockVariablesToPropagate = new LinkedList(); + for (int i = 0; i < labelInstr.dests.count; i++) { + var register = labelInstr.dests.curr[i]; + var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + + defBlockVariablesToPropagate.add(variable); + + var variableLocationList = new VariableLocations(); + variableLocationList.add(register); + defVariableToLocations.put(variable, variableLocationList); + } + + Queue> worklist = new LinkedList<>(); + Set> processed = new HashSet<>(); + worklist.add(defBlock); + propagateMap.put(defBlock, defBlockVariablesToPropagate); + locationMap.put(defBlock, defVariableToLocations); + + while (!worklist.isEmpty()) { + var curr = worklist.remove(); + if (processed.contains(curr)) { + continue; + } + processed.add(curr); + + var state = this.blockStates.get(curr); + var variablesToPropagate = propagateMap.get(curr); + var variableToLocations = locationMap.get(curr); + + var instructions = blockInstructions.get(curr); + for (var instruction : instructions) { + if (curr.equals(defBlock) && instruction.lirInstruction instanceof StandardOp.LabelOp) { + continue; + } + + Value fromLocation; + Value toLocation; + + switch (instruction) { + case RAVInstruction.Move move -> { + fromLocation = move.from; + toLocation = move.to; + } + case RAVInstruction.Spill spill -> { + fromLocation = spill.from; + toLocation = spill.to; + } + case RAVInstruction.StackMove stack -> { + fromLocation = stack.from; + toLocation = stack.to; + } + case RAVInstruction.Reload reload -> { + fromLocation = reload.from; + toLocation = reload.to; + } + case RAVInstruction.VirtualMove virtMove -> { + toLocation = virtMove.location; + fromLocation = null; + } + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + var location = op.dests.curr[i]; + if (location == null) { + continue; + } + + var itToPropagate = variablesToPropagate.iterator(); + while (itToPropagate.hasNext()) { + var variable = LIRValueUtil.asVariable(itToPropagate.next()); + var locations = variableToLocations.get(variable); + locations.remove(location); + } + } + + continue; + } + default -> { + continue; + } + } + + var itToPropagate = variablesToPropagate.iterator(); + while (itToPropagate.hasNext()) { + var variable = LIRValueUtil.asVariable(itToPropagate.next()); + var locations = variableToLocations.get(variable); + if (fromLocation != null && locations.contains(fromLocation)) { + locations.add(toLocation); + } else if (locations.contains(toLocation)) { + locations.remove(toLocation); // Overwritten + } + } + } + + var variablesToBePropagated = new LinkedList(); + var iterator = variablesToPropagate.iterator(); + while (iterator.hasNext()) { + var variable = LIRValueUtil.asVariable(iterator.next()); + var locations = variableToLocations.get(variable); + if (locations.isEmpty()) { + continue; + } + + variablesToBePropagated.add(variable); + for (var location : locations) { + if (state != null) { + state.values.put(location, new ValueAllocationState(variable, labelInstr.lirInstruction)); + } + } + } + + if (variablesToBePropagated.isEmpty()) { + continue; + } + + for (int i = 0; i < curr.getSuccessorCount(); i++) { + var succ = curr.getSuccessorAt(i); + var succEntryState = this.blockEntryStates.get(succ); + if (succEntryState == null) { + continue; + } + + if (succ.equals(defBlock)) { + // This means that the definition block would have same value as predecessor + // for example: B0 defines [v0] in label, B1 is it's successor as well as it's + // predecessor, and if it does not overwrite this register, it would change + // entry state for B0 to include v0, which is defined by B0. + + for (int j = 0; j < labelInstr.dests.count; j++) { + var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[j]); + + if (!variablesToPropagate.contains(variable)) { + continue; + } + + var location = labelInstr.dests.curr[j]; + var locations = variableToLocations.get(variable); + if (locations.contains(location)) { + // Only throw this error if location in label was not overwritten. + throw new CircularDefinitionError(defBlock, curr, labelInstr, variablesToBePropagated); + } + } + + continue; + } + + boolean dominates = succ.dominates(defBlock); + if (dominates) { + continue; + } + + Map newLoc = new HashMap<>(); + var itToBePropagated = variablesToBePropagated.iterator(); + while (itToBePropagated.hasNext()) { + var variable = LIRValueUtil.asVariable(itToBePropagated.next()); + var locations = variableToLocations.get(variable); + for (var location : locations) { + succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr.lirInstruction)); + } + + newLoc.put(variable, new VariableLocations(locations)); + } + + locationMap.put(succ, newLoc); + propagateMap.put(succ, variablesToBePropagated); + worklist.add(succ); + } + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 28e2c4ddcd75..ca3ec5300d57 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -25,14 +25,14 @@ class FromUsageResolverGlobal { protected LIR lir; protected BlockMap> blockInstructions; - public Map> instrToBlockMap; public Map labelMap; public Map defined; public Map reached; public Map> firstUsages; public Map initialLocations; public Map aliasMap; - public BlockMap blockUsageMap; + public Map> aliasBlockMap; + public BlockMap blockUsageMap; // Entry blocks! public List> endBlocks; class BlockUsage { @@ -60,6 +60,11 @@ private BlockUsage merge(BlockUsage other) { while (iterator.hasNext()) { var variable = LIRValueUtil.asVariable(iterator.next()); var defValue = other.locations.get(variable); + + if (Value.ILLEGAL.equals(defValue) && newDefs.locations.containsKey(variable)) { + continue; + } + newDefs.locations.put(variable, defValue); } @@ -71,13 +76,13 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b this.lir = lir; this.blockInstructions = blockInstructions; - this.instrToBlockMap = new HashMap<>(); this.labelMap = new HashMap<>(); this.defined = new HashMap<>(); this.reached = new HashMap<>(); this.firstUsages = new HashMap<>(); this.initialLocations = new HashMap<>(); this.aliasMap = new HashMap<>(); + this.aliasBlockMap = new HashMap<>(); this.endBlocks = new ArrayList<>(); this.blockUsageMap = new BlockMap<>(lir.getControlFlowGraph()); @@ -99,7 +104,7 @@ public void resolvePhiFromUsage() { while (!worklist.isEmpty()) { var block = worklist.remove(); - var usage = blockUsageMap.get(block); + var usage = new BlockUsage(blockUsageMap.get(block)); var instructions = blockInstructions.get(block); for (var instruction : instructions.reversed()) { switch (instruction) { @@ -109,26 +114,10 @@ public void resolvePhiFromUsage() { case RAVInstruction.StackMove stackMove -> handleMove(usage, stackMove.from, stackMove.to); case RAVInstruction.Op op -> { if (op.lirInstruction instanceof StandardOp.LabelOp) { - for (int i = 0; i < op.dests.count; i++) { - if (!LIRValueUtil.isVariable(op.dests.orig[i])) { - continue; - } - - var variable = LIRValueUtil.asVariable(op.dests.orig[i]); - if (usage.locations.get(variable) == null) { - continue; - } - - if (op.dests.curr[i] != null) { - continue; - } - - resolveVariable(usage, variable); - usage.locations.put(variable, null); - } - + this.resolveLabel(usage, op, block); continue; } + // TODO: decide how to deal with locations being overwritten. if (firstUsages.containsKey(op)) { var iterator = firstUsages.get(op).iterator(); @@ -184,8 +173,6 @@ protected void initializeUsages() { var instructions = blockInstructions.get(block); var label = (RAVInstruction.Op) instructions.getFirst(); - instrToBlockMap.put(label, block); - for (var i = 0; i < label.dests.count; i++) { if (LIRValueUtil.isVariable(label.dests.orig[i])) { var variable = LIRValueUtil.asVariable(label.dests.orig[i]); @@ -202,6 +189,7 @@ protected void initializeUsages() { handleUsages(op.uses, op, block); handleUsages(op.alive, op, block); + handleUsages(op.stateValues, op, block); } } @@ -217,6 +205,7 @@ protected void initializeUsages() { var succ = block.getSuccessorAt(0); var succLabel = (RAVInstruction.Op) blockInstructions.get(succ).getFirst(); + aliasBlockMap.put(variable, block); aliasMap.put(variable, LIRValueUtil.asVariable(succLabel.dests.orig[i])); } } @@ -252,7 +241,6 @@ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction } firstUsages.get(op).add(variable); - instrToBlockMap.put(op, block); initialLocations.put(variable, values.curr[i]); aliasMap.remove(variable); @@ -264,7 +252,7 @@ protected void handleMove(BlockUsage usage, Value from, Value to) { for (var entry : usage.locations.entrySet()) { var variable = LIRValueUtil.asVariable(entry.getKey()); var location = entry.getValue(); - if (location == null) { + if (location == null || Value.ILLEGAL.equals(location)) { continue; } @@ -274,21 +262,26 @@ protected void handleMove(BlockUsage usage, Value from, Value to) { } } - protected void resolveVariable(BlockUsage usage, Variable variable) { - var label = labelMap.get(variable); - var block = instrToBlockMap.get(label); - var location = usage.locations.get(variable); - + protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBlock block) { for (int i = 0; i < label.dests.count; i++) { if (!LIRValueUtil.isVariable(label.dests.orig[i])) { continue; } - var labelVar = LIRValueUtil.asVariable(label.dests.orig[i]); - if (!labelVar.equals(variable)) { + var variable = LIRValueUtil.asVariable(label.dests.orig[i]); + if (usage.locations.get(variable) == null) { + continue; + } + + if (label.dests.curr[i] != null) { continue; } + var location = usage.locations.get(variable); + if (location == null || Value.ILLEGAL.equals(location)) { + throw new IllegalStateException(); + } + label.dests.curr[i] = location; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); @@ -297,20 +290,26 @@ protected void resolveVariable(BlockUsage usage, Variable variable) { jump.alive.curr[i] = location; } - break; - } - - // Variables that are passed into jumps without any other usage are aliases - // for same variable in successor label, whenever said variable is resolved - // we now have a location for this variable and can take other moves into account. - for (var entry : aliasMap.entrySet()) { - var aliased = LIRValueUtil.asVariable(entry.getValue()); - if (variable.equals(aliased)) { - var alias = LIRValueUtil.asVariable(entry.getKey()); + // Variables that are passed into jumps without any other usage are aliases + // for same variable in successor label, whenever said variable is resolved + // we now have a location for this variable and can take other moves into account. + for (var entry : aliasMap.entrySet()) { + var aliased = LIRValueUtil.asVariable(entry.getValue()); + if (variable.equals(aliased)) { + var alias = LIRValueUtil.asVariable(entry.getKey()); + var aliasBlock = aliasBlockMap.get(alias); + + if (blockUsageMap.get(aliasBlock) == null) { + this.blockUsageMap.put(aliasBlock, new BlockUsage()); + } - usage.locations.put(alias, location); - usage.reached.add(alias); + var aliasBlockUsage = blockUsageMap.get(aliasBlock); + aliasBlockUsage.locations.put(alias, location); + aliasBlockUsage.reached.add(alias); + } } + + usage.locations.put(variable, null); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java new file mode 100644 index 000000000000..0957f79320b2 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java @@ -0,0 +1,151 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.meta.Value; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class LabelConflictResolver implements ConflictResolver { + protected LIR lir; + protected BlockMap> blockInstructions; + + protected Map labelMap; + protected Map> rules; + protected Map> expandedRules; + + public LabelConflictResolver() { + this.labelMap = new HashMap<>(); + this.rules = new HashMap<>(); + this.expandedRules = new HashMap<>(); + } + + @Override + public void prepare(LIR lir, BlockMap> blockInstructions) { + this.lir = lir; + this.blockInstructions = blockInstructions; + + this.buildLabelMap(); + this.buildRules(); + this.expandRules(); + } + + protected void buildLabelMap() { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + + for (int i = 0; i < labelInstr.dests.count; i++) { + if (!LIRValueUtil.isVariable(labelInstr.dests.orig[i])) { + continue; + } + + var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + labelMap.put(labelVariable, labelInstr); + } + } + } + + protected void buildRules() { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + + for (int i = 0; i < labelInstr.dests.count; i++) { + if (!LIRValueUtil.isVariable(labelInstr.dests.orig[i])) { + continue; + } + + var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + Set resolutions = new HashSet<>(); + + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var predInstructions = blockInstructions.get(pred); + var jumpInstr = (RAVInstruction.Op) predInstructions.getLast(); + + resolutions.add(jumpInstr.alive.orig[i]); + } + + rules.put(labelVariable, resolutions); + } + } + } + + protected void expandRules() { + for (var entry : rules.entrySet()) { + var variable = LIRValueUtil.asVariable(entry.getKey()); + + ArrayList sources = new ArrayList<>(entry.getValue()); + int sourceCount = sources.size(); + for (int i = 0; i < sourceCount; i++) { + var source = sources.get(i); + if (!LIRValueUtil.isVariable(source)) { + continue; + } + + var sourceVariable = LIRValueUtil.asVariable(source); + if (rules.containsKey(sourceVariable)) { + for (var newSource : rules.get(sourceVariable)) { + if (sources.contains(newSource)) { + continue; + } + + sources.add(newSource); + sourceCount++; + } + } + } + + expandedRules.put(variable, new HashSet<>(sources)); + } + } + + protected boolean isSubsetOfValues(Set superset, Set subset) { + for (var v : subset) { + if (!superset.contains(v.getValue())) { + return false; + } + } + return true; + } + + @Override + public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState) { + if (!this.expandedRules.containsKey(target)) { + return null; + } + + var ruleSet = this.expandedRules.get(target); + var confStates = conflictedState.getConflictedStates(); + + if (!this.isSubsetOfValues(ruleSet, confStates)) { + return null; + } + + return new ValueAllocationState(target, labelMap.get(target).getLIRInstruction()); + } + + @Override + public ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState) { + if (!this.expandedRules.containsKey(target)) { + return null; + } + + var ruleSet = this.expandedRules.get(target); + if (!ruleSet.contains(valueState.getValue())) { + return null; + } + + return new ValueAllocationState(target, labelMap.get(target).getLIRInstruction()); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 852428f129a3..5d6ba93886ab 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -12,6 +12,7 @@ import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Value; +import jdk.vm.ci.meta.ValueKind; public class MergedBlockVerifierState { public MergedAllocationStateMap values; @@ -20,18 +21,21 @@ public class MergedBlockVerifierState { protected RegisterAllocationConfig registerAllocationConfig; protected ConflictResolver conflictConstantResolver; + protected ConflictResolver labelConflictResolver; - public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver) { + public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { this.values = new MergedAllocationStateMap(); this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; + this.labelConflictResolver = labelConflictResolver; } - protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver) { + protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; + this.labelConflictResolver = labelConflictResolver; if (other == null) { this.values = new MergedAllocationStateMap(); @@ -57,13 +61,18 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. assert orig != null; + boolean isJump = op.lirInstruction instanceof StandardOp.JumpOp; if (curr == null) { - if (op.lirInstruction instanceof StandardOp.JumpOp) { - if (phiResolution == PhiResolution.FromUsage) { + if (isJump) { + if (phiResolution == PhiResolution.FromUsage || phiResolution == PhiResolution.FromUsageGlobal) { // Variable has no usage, thus no location present. continue; } + if (phiResolution == PhiResolution.ByAllocator || phiResolution == PhiResolution.FromConflicts) { + continue; + } + throw new LabelNotResolvedError(block, labelOp, phiResolution); } @@ -75,7 +84,7 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. continue; } - if (!kindsEqual(orig, curr)) { + if (!kindsEqual(orig, curr) && !isJump) { throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); } @@ -87,39 +96,44 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. if (state.isConflicted()) { var variable = LIRValueUtil.asVariable(orig); var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state); - if (resolvedState == null) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + + if (phiResolution == PhiResolution.FromConflicts && resolvedState == null) { + resolvedState = this.labelConflictResolver.resolveConflictedState(variable, (ConflictedAllocationState) state); + if (resolvedState == null) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } } - this.values.put(curr, new ValueAllocationState(variable, op.lirInstruction)); + this.values.put(curr, resolvedState); continue; } if (state instanceof ValueAllocationState valAllocState) { - if (!kindsEqual(orig, valAllocState.value) && !(op.lirInstruction instanceof StandardOp.JumpOp)) { - if (!(orig instanceof CastValue castOrig && kindsEqual(castOrig.underlyingValue(), valAllocState.value))) { - // If orig was recast, it does not matter for this validation - // as we just check that the type before is correct - // TODO: sort this code better - throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); - } + if (!kindsEqualFromState(orig, valAllocState.value)) { + throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); } if (!valAllocState.value.equals(orig)) { if (orig instanceof CastValue castValue && valAllocState.value.equals(castValue.underlyingValue())) { - // check for underlying value for CastValue. - continue; + continue; // They aren't equal here because of the CastValue, so if they are equal afterwards, we skip next part. } if (valAllocState.value instanceof ConstantValue) { var variable = LIRValueUtil.asVariable(orig); var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState); - if (resolvedState == null) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + if (resolvedState != null) { + this.values.put(curr, resolvedState); + continue; } + } - this.values.put(curr, new ValueAllocationState(variable, op.lirInstruction)); - continue; + if (phiResolution == PhiResolution.FromConflicts && LIRValueUtil.isVariable(orig)) { + var variable = LIRValueUtil.asVariable(orig); + var resolvedState = labelConflictResolver.resolveValueState(variable, valAllocState); + if (resolvedState != null) { + this.values.put(curr, resolvedState); + continue; + } } throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); @@ -144,13 +158,25 @@ protected boolean kindsEqual(Value orig, Value curr) { // TestCase: BoxingTest.boxShort // MOV (x: [v11|QWORD[.] + 12], y: reinterpret: v0|DWORD as: WORD) size: WORD // MOV (x: [rax|QWORD[.] + 12], y: r10|WORD(DWORD)) size: WORD - // TODO: figure out the correct semantics for these casts return origKind.getPlatformKind().equals(currKind.getPlatformKind()); } + // TODO: maybe we should also look at the type before cast + // LIRKindWithCast.getActualKind() + // CastValue.underlyingValue().getValueKind() return false; } + protected boolean kindsEqualFromState(Value orig, Value fromState) { + ValueKind origKind = orig.getValueKind(); + ValueKind currKind = fromState.getValueKind(); + if (orig instanceof CastValue castOrig) { + origKind = castOrig.underlyingValue().getValueKind(); + } + + return origKind.equals(currKind); + } + protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock block) { for (int i = 0; i < instruction.alive.count; i++) { Value value = instruction.alive.curr[i]; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java index 1a0ff18b47ac..ef162a0af02f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java @@ -3,5 +3,8 @@ public enum PhiResolution { FromJump, FromUsage, - FromPredecessors + FromPredecessors, + FromUsageGlobal, + FromConflicts, + ByAllocator } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index cacb2e34f5b7..5c84a52a75da 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -4,66 +4,50 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; -import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Queue; -import java.util.Set; -public final class RegisterAllocationVerifier { - public BlockMap> blockInstructions; - public BlockMap blockStates; // Current states - public BlockMap blockEntryStates; // State on entry to block - - public LIR lir; - public PhiResolution phiResolution; - - // FromUsage resolver - public Map labelMap; - public Map variableRegisterMap; - public Map usageAliasMap; - - // FromPredecessors resolver - public BlockMap blockDefinitions; - - public FromUsageResolverGlobal fromUsageResolver; +public class RegisterAllocationVerifier { + protected BlockMap> blockInstructions; + protected BlockMap blockStates; + protected BlockMap blockEntryStates; + protected LIR lir; protected RegisterAllocationConfig registerAllocationConfig; + protected PhiResolution phiResolution; + + protected FromPredecessorsResolver fromPredecessorsResolver; + protected FromJumpResolver fromJumpResolver; + protected FromUsageResolver fromUsageResolver; + protected FromUsageResolverGlobal fromUsageResolverGlobal; protected ConflictResolver constantMaterializationConflictResolver; + protected ConflictResolver labelConflictResolver; public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; + this.registerAllocationConfig = registerAllocationConfig; this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); + this.labelConflictResolver = new LabelConflictResolver(); var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); - this.blockEntryStates.put(cfg.getStartBlock(), new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantMaterializationConflictResolver)); + this.blockStates = new BlockMap<>(cfg); this.phiResolution = phiResolution; - this.blockDefinitions = new BlockMap<>(cfg); - - this.labelMap = new HashMap<>(); - this.variableRegisterMap = new HashMap<>(); - this.usageAliasMap = new HashMap<>(); - - this.fromUsageResolver = new FromUsageResolverGlobal(lir, blockInstructions); - this.registerAllocationConfig = registerAllocationConfig; + this.fromPredecessorsResolver = new FromPredecessorsResolver(lir, blockInstructions, blockStates, blockEntryStates); + this.fromJumpResolver = new FromJumpResolver(lir, blockInstructions, blockStates); + this.fromUsageResolver = new FromUsageResolver(lir, blockInstructions); + this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); } private boolean doPrecessorsHaveStates(BasicBlock block) { @@ -123,383 +107,6 @@ public BlockMap getDefinitionSets() { return blockDefinitions; } - /** - * Fill in missing variable locations for current block's label instruction and predecessor - * jump instructions. - *

- * We are looking for intersection between locations of individual processors, this should - * give us a single register that is used for the phi, and is necessary for jump to verify - * that contents are alive that that point and for label to define where the result of phi - * will be held to used in said block. - * - * @param block Block that needs phi function output - * @param labelInstr Label instruction of said block - */ - private boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { - for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - var inputValue = jump.alive.orig[i]; - - var varLoc = state.values.getValueLocations(inputValue); - if (locations == null) { - locations = varLoc; - continue; - } - - locations.retainAll(varLoc); - } - - Value location = null; - if (locations.size() != 1) { - if (locations.isEmpty()) { - return false; - } - - for (int j = 0; j < block.getPredecessorCount(); j++) { - int time = -1; - Value blockReg = null; - for (var loc : locations) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - - var regTime = state.values.getKeyTime(loc); - if (regTime > time) { - // TODO: replace time with priority of Moves inserted by the Register Allocator. - time = regTime; // Max time - blockReg = loc; - } - } - - if (location == null) { - location = blockReg; - } else if (!location.equals(blockReg)) { - // Not same for all blocks, so none choosen. - return false; - } - } - } else { - location = locations.stream().findFirst().get(); - } - - var registerValue = location; - // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); - - labelInstr.dests.curr[i] = registerValue; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = registerValue; - } - } - - return true; - } - - class VariableLocations implements Iterable { - protected Set internalList; - protected Map valueMap; - - public VariableLocations() { - this.internalList = new HashSet<>(); - this.valueMap = new HashMap<>(); - } - - public VariableLocations(VariableLocations other) { - this.internalList = new HashSet<>(other.internalList); - this.valueMap = new HashMap<>(other.valueMap); - } - - public void add(Value location) { - var locString = getValueKeyString(location); - internalList.add(locString); - valueMap.put(locString, location); - } - - public void remove(Value location) { - var locString = getValueKeyString(location); - internalList.remove(locString); - valueMap.remove(locString); - } - - public boolean isEmpty() { - return internalList.isEmpty(); - } - - public boolean contains(Value location) { - var locString = getValueKeyString(location); - return valueMap.containsKey(locString); - } - - protected String getValueKeyString(Value value) { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); - } - - return value.toString(); - } - - @Override - public Iterator iterator() { - return internalList.stream().map(valueMap::get).iterator(); - } - } - - private void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { - var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); - - // Definition block needs to have this set. - var propagateMap = new HashMap, List>(); - var locationMap = new HashMap, Map>(); - - var defVariableToLocations = new HashMap(); - var defBlockVariablesToPropagate = new LinkedList(); - for (int i = 0; i < labelInstr.dests.count; i++) { - var register = labelInstr.dests.curr[i]; - var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - - defBlockVariablesToPropagate.add(variable); - - var variableLocationList = new VariableLocations(); - variableLocationList.add(register); - defVariableToLocations.put(variable, variableLocationList); - } - - Queue> worklist = new LinkedList<>(); - Set> processed = new HashSet<>(); - worklist.add(defBlock); - propagateMap.put(defBlock, defBlockVariablesToPropagate); - locationMap.put(defBlock, defVariableToLocations); - - while (!worklist.isEmpty()) { - var curr = worklist.remove(); - if (processed.contains(curr)) { - continue; - } - processed.add(curr); - - var state = this.blockStates.get(curr); - var variablesToPropagate = propagateMap.get(curr); - var variableToLocations = locationMap.get(curr); - - var instructions = blockInstructions.get(curr); - for (var instruction : instructions) { - if (curr.equals(defBlock) && instruction.lirInstruction instanceof StandardOp.LabelOp) { - continue; - } - - Value fromLocation; - Value toLocation; - - switch (instruction) { - case RAVInstruction.Move move -> { - fromLocation = move.from; - toLocation = move.to; - } - case RAVInstruction.Spill spill -> { - fromLocation = spill.from; - toLocation = spill.to; - } - case RAVInstruction.StackMove stack -> { - fromLocation = stack.from; - toLocation = stack.to; - } - case RAVInstruction.Reload reload -> { - fromLocation = reload.from; - toLocation = reload.to; - } - case RAVInstruction.VirtualMove virtMove -> { - toLocation = virtMove.location; - fromLocation = null; - } - case RAVInstruction.Op op -> { - for (int i = 0; i < op.dests.count; i++) { - var location = op.dests.curr[i]; - if (location == null) { - continue; - } - - var itToPropagate = variablesToPropagate.iterator(); - while (itToPropagate.hasNext()) { - var variable = LIRValueUtil.asVariable(itToPropagate.next()); - var locations = variableToLocations.get(variable); - locations.remove(location); - } - } - - continue; - } - default -> { - continue; - } - } - - var itToPropagate = variablesToPropagate.iterator(); - while (itToPropagate.hasNext()) { - var variable = LIRValueUtil.asVariable(itToPropagate.next()); - var locations = variableToLocations.get(variable); - if (fromLocation != null && locations.contains(fromLocation)) { - locations.add(toLocation); - } else if (locations.contains(toLocation)) { - locations.remove(toLocation); // Overwritten - } - } - } - - var variablesToBePropagated = new LinkedList(); - var iterator = variablesToPropagate.iterator(); - while (iterator.hasNext()) { - var variable = LIRValueUtil.asVariable(iterator.next()); - var locations = variableToLocations.get(variable); - if (locations.isEmpty()) { - continue; - } - - variablesToBePropagated.add(variable); - for (var location : locations) { - if (state != null) { - state.values.put(location, new ValueAllocationState(variable, labelInstr.lirInstruction)); - } - } - } - - if (variablesToBePropagated.isEmpty()) { - continue; - } - - for (int i = 0; i < curr.getSuccessorCount(); i++) { - var succ = curr.getSuccessorAt(i); - var succEntryState = this.blockEntryStates.get(succ); - if (succEntryState == null) { - continue; - } - - if (succ.equals(defBlock)) { - // This means that the definition block would have same value as predecessor - // for example: B0 defines [v0] in label, B1 is it's successor as well as it's - // predecessor, and if it does not overwrite this register, it would change - // entry state for B0 to include v0, which is defined by B0. - - for (int j = 0; j < labelInstr.dests.count; j++) { - var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[j]); - - if (!variablesToPropagate.contains(variable)) { - continue; - } - - var location = labelInstr.dests.curr[j]; - var locations = variableToLocations.get(variable); - if (locations.contains(location)) { - // Only throw this error if location in label was not overwritten. - throw new CircularDefinitionError(defBlock, curr, labelInstr, variablesToBePropagated); - } - } - - continue; - } - - boolean dominates = succ.dominates(defBlock); - if (dominates) { - continue; - } - - Map newLoc = new HashMap<>(); - var itToBePropagated = variablesToBePropagated.iterator(); - while (itToBePropagated.hasNext()) { - var variable = LIRValueUtil.asVariable(itToBePropagated.next()); - var locations = variableToLocations.get(variable); - for (var location : locations) { - succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr.lirInstruction)); - } - - newLoc.put(variable, new VariableLocations(locations)); - } - - locationMap.put(succ, newLoc); - propagateMap.put(succ, variablesToBePropagated); - worklist.add(succ); - } - } - } - - /** - * Resolve phi registers from this jump point, can only be done if - * the phi variables/constants only have one location. - * - *

- * This way we can handle phi arguments before we reach said block. - * But cannot really be done for loops, if their initial values - * are the same and there are multiple ones. - *

- * - * @param block From whom we are jumping from - */ - private void resolvePhiFromJump(BasicBlock block) { - var state = this.blockStates.get(block); - var jumpInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getLast(); - - for (int i = 0; i < jumpInstr.alive.count; i++) { - if (jumpInstr.alive.curr[i] != null) { - continue; - } - - var inputValue = jumpInstr.alive.orig[i]; - if (!LIRValueUtil.isVariable(inputValue) && !(inputValue instanceof ConstantValue)) { - continue; - } - - var registerValue = this.getRegisterBeforeJump(state, inputValue); - if (registerValue == null) { - return; - } - - jumpInstr.alive.curr[i] = registerValue; - for (int j = 0; j < block.getSuccessorCount(); j++) { - var succ = block.getSuccessorAt(j); - var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); - - labelInstr.dests.curr[i] = jumpInstr.alive.curr[i]; - - for (int k = 0; k < succ.getPredecessorCount(); k++) { - // Sibling jumps need to be updated as well in order to pass input checks. - var sibling = succ.getPredecessorAt(k); - if (sibling.equals(block)) { - continue; - } - - var siblingJumpInstr = (RAVInstruction.Op) this.blockInstructions.get(sibling).getLast(); - siblingJumpInstr.alive.curr[i] = jumpInstr.alive.curr[i]; - } - } - } - } - - private Value getRegisterBeforeJump(MergedBlockVerifierState state, Value inputValue) { - // Does not work for constants if there is more of them - var locations = state.values.getValueLocations(inputValue); - if (locations.isEmpty()) { - return null; - } - - var register = locations.stream().findFirst().get(); - if (locations.size() != 1) { - int time = -1; - for (var location : locations) { - var locTime = state.values.getKeyTime(location); - if (locTime > time) { - register = location; - time = locTime; - } - } - } - - return register; - // return register.asValue(inputValue.getValueKind()); - } - private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { return op.hasMissingDefinitions(); } @@ -513,6 +120,8 @@ private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { */ public void calculateEntryBlocks() { Queue> worklist = new LinkedList<>(); + + this.blockEntryStates.put(this.lir.getControlFlowGraph().getStartBlock(), new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantMaterializationConflictResolver, labelConflictResolver)); worklist.add(this.lir.getControlFlowGraph().getStartBlock()); while (!worklist.isEmpty()) { @@ -522,7 +131,7 @@ public void calculateEntryBlocks() { boolean resolvedPhi = false; var labelInstr = (RAVInstruction.Op) instructions.getFirst(); if (this.phiResolution == PhiResolution.FromPredecessors && this.doPrecessorsHaveStates(block) && this.hasMissingRegistersInLabel(labelInstr)) { - resolvedPhi = this.resolvePhiFromPredecessors(block, labelInstr); + resolvedPhi = this.fromPredecessorsResolver.resolvePhiFromPredecessors(block, labelInstr); } if (this.phiResolution == PhiResolution.FromJump && this.hasMissingRegistersInLabel(labelInstr)) { @@ -533,18 +142,19 @@ public void calculateEntryBlocks() { } // Create new entry state for successor blocks out of current block state - var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), registerAllocationConfig, phiResolution, constantMaterializationConflictResolver); + var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), registerAllocationConfig, phiResolution, constantMaterializationConflictResolver, labelConflictResolver); for (var instr : instructions) { state.update(instr, block); } this.blockStates.put(block, state); if (this.phiResolution == PhiResolution.FromJump) { - this.resolvePhiFromJump(block); + // TODO: Sibling in worklist, then process and select resolut in cooperation + this.fromJumpResolver.resolvePhiFromJump(block); } if (resolvedPhi) { - this.propagateLabelChangeFromPredecessors(block); + this.fromPredecessorsResolver.propagateLabelChangeFromPredecessors(block); } for (int i = 0; i < block.getSuccessorCount(); i++) { @@ -552,7 +162,7 @@ public void calculateEntryBlocks() { MergedBlockVerifierState succState; if (this.blockEntryStates.get(succ) == null) { - succState = new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantMaterializationConflictResolver); + succState = new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantMaterializationConflictResolver, labelConflictResolver); // Either there's no state because it was not yet processed first part of the condition // or, we need to reset it because label changed, second part of the condition @@ -673,16 +283,16 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { public void run() { this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); + if (this.phiResolution == PhiResolution.FromConflicts) { + this.labelConflictResolver.prepare(lir, blockInstructions); + } if (this.phiResolution == PhiResolution.FromUsage) { this.fromUsageResolver.resolvePhiFromUsage(); } - if (this.phiResolution == PhiResolution.FromPredecessors) { - // Currently only useful to this resolution type, but later - // we should only go through instructions once and then just merge - // with entry state and successors. - this.blockDefinitions = this.getDefinitionSets(); + if (this.phiResolution == PhiResolution.FromUsageGlobal) { + this.fromUsageResolverGlobal.resolvePhiFromUsage(); } this.calculateEntryBlocks(); From 13fbbf6b3fdff9419c8d2c82feff8c9151036365 Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 30 Jan 2026 23:42:55 +0100 Subject: [PATCH 034/112] Use more predecessors to resolve FromJump --- .../lir/alloc/verifier/FromJumpResolver.java | 112 +++++++++--------- .../verifier/RegisterAllocationVerifier.java | 69 +++-------- 2 files changed, 72 insertions(+), 109 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java index 53ba9d953f16..3c87ce263a5d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java @@ -8,6 +8,7 @@ import jdk.vm.ci.meta.Value; import java.util.List; +import java.util.Set; public class FromJumpResolver { public LIR lir; @@ -20,77 +21,76 @@ public FromJumpResolver(LIR lir, BlockMap> blockInstru this.blockStates = blockStates; } - /** - * Resolve phi registers from this jump point, can only be done if - * the phi variables/constants only have one location. - * - *

- * This way we can handle phi arguments before we reach said block. - * But cannot really be done for loops, if their initial values - * are the same and there are multiple ones. - *

- * - * @param block From whom we are jumping from - */ - public void resolvePhiFromJump(BasicBlock block) { - var state = this.blockStates.get(block); - var jumpInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getLast(); + public void resolvePhi(BasicBlock block) { + var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getFirst(); + for (int i = 0; i < labelInstr.dests.count; i++) { + Set locations = null; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + if (state == null) { + continue; + } - for (int i = 0; i < jumpInstr.alive.count; i++) { - if (jumpInstr.alive.curr[i] != null) { - continue; - } + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + var inputValue = jump.alive.orig[i]; - var inputValue = jumpInstr.alive.orig[i]; - if (!LIRValueUtil.isVariable(inputValue) && !(inputValue instanceof ConstantValue)) { - continue; + var varLoc = state.values.getValueLocations(inputValue); + if (locations == null) { + locations = varLoc; + continue; + } + + locations.retainAll(varLoc); } - var registerValue = this.getRegisterBeforeJump(state, inputValue); - if (registerValue == null) { - return; + if (locations == null) { + continue; } - jumpInstr.alive.curr[i] = registerValue; - for (int j = 0; j < block.getSuccessorCount(); j++) { - var succ = block.getSuccessorAt(j); - var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); + Value location = null; + if (locations.size() != 1) { + if (locations.isEmpty()) { + return; + } - labelInstr.dests.curr[i] = jumpInstr.alive.curr[i]; + for (int j = 0; j < block.getPredecessorCount(); j++) { + int time = -1; + Value blockReg = null; + for (var loc : locations) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + if (state == null) { + continue; + } - for (int k = 0; k < succ.getPredecessorCount(); k++) { - // Sibling jumps need to be updated as well in order to pass input checks. - var sibling = succ.getPredecessorAt(k); - if (sibling.equals(block)) { - continue; + var regTime = state.values.getKeyTime(loc); + if (regTime > time) { + time = regTime; // Max time + blockReg = loc; + } } - var siblingJumpInstr = (RAVInstruction.Op) this.blockInstructions.get(sibling).getLast(); - siblingJumpInstr.alive.curr[i] = jumpInstr.alive.curr[i]; + if (location == null) { + location = blockReg; + } else if (!location.equals(blockReg)) { + // Not same for all blocks, so none choosen. + return; + } } + } else { + location = locations.stream().findFirst().get(); } - } - } - private Value getRegisterBeforeJump(MergedBlockVerifierState state, Value inputValue) { - // Does not work for constants if there is more of them - var locations = state.values.getValueLocations(inputValue); - if (locations.isEmpty()) { - return null; - } + var registerValue = location; + // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); - var register = locations.stream().findFirst().get(); - if (locations.size() != 1) { - int time = -1; - for (var location : locations) { - var locTime = state.values.getKeyTime(location); - if (locTime > time) { - register = location; - time = locTime; - } + labelInstr.dests.curr[i] = registerValue; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + jump.alive.curr[i] = registerValue; } } - - return register; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 5c84a52a75da..7f5aab9384b6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -60,53 +60,6 @@ private boolean doPrecessorsHaveStates(BasicBlock block) { return true; } - public BlockMap getDefinitionSets() { - BlockMap blockDefinitions = new BlockMap<>(this.lir.getControlFlowGraph()); - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = blockInstructions.get(block); - var definitions = new DefinitionSet(); - - for (var instruction : instructions) { - switch (instruction) { - case RAVInstruction.Op op -> { - for (int i = 0; i < op.dests.count; i++) { - var location = op.dests.curr[i]; - if (location == null) { - continue; - } - - definitions.add(location); - } - - for (int i = 0; i < op.temp.count; i++) { - var location = op.temp.curr[i]; - if (location == null || Value.ILLEGAL.equals(location)) { - continue; - } - - definitions.add(location); - } - } - case RAVInstruction.Move move -> definitions.add(move.to); - case RAVInstruction.Reload reload -> definitions.add(reload.to); - case RAVInstruction.Spill spill -> definitions.add(spill.to); - case RAVInstruction.StackMove stackMove -> definitions.add(stackMove.to); - case RAVInstruction.VirtualMove virtMove -> { - if (!LIRValueUtil.isVariable(virtMove.location)) { - definitions.add(virtMove.location); - } - } - default -> GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); - } - } - - blockDefinitions.put(block, definitions); - } - - return blockDefinitions; - } - private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { return op.hasMissingDefinitions(); } @@ -135,10 +88,21 @@ public void calculateEntryBlocks() { } if (this.phiResolution == PhiResolution.FromJump && this.hasMissingRegistersInLabel(labelInstr)) { - if (!doPrecessorsHaveStates(block)) { - // A hot fix, if not all predecessors have state, then we wait until all of them do + boolean skip = false; + for (int i = 0; i < block.getPredecessorCount(); i++) { + var pred = block.getPredecessorAt(i); + if (worklist.contains(pred)) { + skip = true; + worklist.add(block); + break; + } + } + + if (skip) { continue; } + + this.fromJumpResolver.resolvePhi(block); } // Create new entry state for successor blocks out of current block state @@ -148,10 +112,9 @@ public void calculateEntryBlocks() { } this.blockStates.put(block, state); - if (this.phiResolution == PhiResolution.FromJump) { - // TODO: Sibling in worklist, then process and select resolut in cooperation - this.fromJumpResolver.resolvePhiFromJump(block); - } + // if (this.phiResolution == PhiResolution.FromJump) { + // this.fromJumpResolver.resolvePhiFromJump(block); + // } if (resolvedPhi) { this.fromPredecessorsResolver.propagateLabelChangeFromPredecessors(block); From a99f31d004ba7833d815e6760791470d0f8d1700 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 31 Jan 2026 00:03:39 +0100 Subject: [PATCH 035/112] Handle canRematerializeToStack for conflict resolution --- .../lir/alloc/verifier/ConflictResolver.java | 5 ++- ...nstantMaterializationConflictResolver.java | 41 ++++++++++++++++--- .../alloc/verifier/LabelConflictResolver.java | 4 +- .../verifier/MergedBlockVerifierState.java | 8 ++-- 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index f156e8121080..cf0558645c64 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -3,11 +3,12 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.meta.Value; import java.util.List; public interface ConflictResolver { void prepare(LIR lir, BlockMap> blockInstructions); - ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState); - ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState); + ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState, Value location); + ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 7e4b4085a300..29ae32b7a328 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -7,16 +7,23 @@ import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.meta.Value; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; public class ConstantMaterializationConflictResolver implements ConflictResolver { protected Map constantVariableMap; + protected Set canRematerializeToStack; public ConstantMaterializationConflictResolver() { this.constantVariableMap = new HashMap<>(); + this.canRematerializeToStack = new HashSet<>(); } @Override @@ -34,7 +41,11 @@ public void prepare(LIR lir, BlockMap> blockInstructio public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); - // TODO: loadConstantOp.canRematerializeToStack? + + // TODO: Handle moves for canRematerializeToStack + // MOVE vstack:1, const // v1 + // MOVE rax, vstack:1 + // USE v1 if (!LIRValueUtil.isVariable(op.dests.orig[0])) { return; @@ -44,11 +55,14 @@ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock bloc var constantValue = new ConstantValue(variable.getValueKind(), loadConstantOp.getConstant()); constantVariableMap.put(variable, constantValue); + if (loadConstantOp.canRematerializeToStack()) { + canRematerializeToStack.add(variable); + } } } @Override - public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState) { + public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location) { var confStates = conflictedState.getConflictedStates(); Variable variable = null; @@ -83,23 +97,38 @@ public ValueAllocationState resolveConflictedState(Variable target, ConflictedAl return null; } + if (isRematerializedToWrongLocation(variable, location)) { + throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); + } + return new ValueAllocationState(variable, null); } @Override - public ValueAllocationState resolveValueState(Variable original, ValueAllocationState valueState) { - if (!this.constantVariableMap.containsKey(original)) { + public ValueAllocationState resolveValueState(Variable variable, ValueAllocationState valueState, Value location) { + if (!this.constantVariableMap.containsKey(variable)) { return null; } if (valueState.getValue() instanceof ConstantValue constant) { - if (!this.constantVariableMap.get(original).equals(constant)) { + if (!this.constantVariableMap.get(variable).equals(constant)) { return null; } - return new ValueAllocationState(original, valueState.source); + if (isRematerializedToWrongLocation(variable, location)) { + throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); + } + + return new ValueAllocationState(variable, valueState.source); } return null; } + + protected boolean isRematerializedToWrongLocation(Variable variable, Value location) { + if (location instanceof StackSlot || location instanceof VirtualStackSlot) { + return !canRematerializeToStack.contains(variable); + } + return false; + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java index 0957f79320b2..aa10d5e9d3f8 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java @@ -120,7 +120,7 @@ protected boolean isSubsetOfValues(Set superset, Set Date: Sat, 31 Jan 2026 21:18:26 +0100 Subject: [PATCH 036/112] Dump debug file on error --- .../RegisterAllocationVerifierPhase.java | 27 ++++++++++- .../lir/alloc/verifier/VerifierPrinter.java | 47 +++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 2d69eae01d24..3c3621d107e7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -2,6 +2,7 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; @@ -18,6 +19,8 @@ import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; +import java.io.FileNotFoundException; +import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -60,7 +63,29 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo var instructions = getVerifierInstructions(lirGenRes); var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.state.phiResolution, context.registerAllocationConfig); - verifier.run(); + // For timers for time spent in pre-alloc and verification phases look for these metric keys: + // LIRPhaseTime_PreRegisterAllocationPhase & LIRPhaseTime_RegisterAllocationVerifierPhase + try { + verifier.run(); + } catch (RAVException | RAVError e) { + var lir = lirGenRes.getLIR(); + var debugCtx = lir.getDebug(); + + if (debugCtx.isDumpEnabled(DebugContext.VERBOSE_LEVEL)) { + var debugPath = debugCtx.getDumpPath(".rav.txt", false); + + try { + PrintStream output = new PrintStream(debugPath); + output.println("Register Allocation Verification failure:"); + output.println(e.getMessage()); + output.println(); + VerifierPrinter.print(output, lir, instructions); + } catch (FileNotFoundException ignored) { + } + } + + throw e; + } } protected BlockMap> getVerifierInstructions(LIRGenerationResult lirGenRes) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java new file mode 100644 index 000000000000..4604e61a0cb0 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -0,0 +1,47 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; + +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; + +public class VerifierPrinter { + public static void print(PrintStream out, LIR lir, BlockMap> instructions) { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + + var blockHeaderSB = new StringBuilder(); + blockHeaderSB.append(block.toString()).append(": "); + + if (block.getPredecessorCount() > 0) { + blockHeaderSB.append(" <- "); + for (int i = 0; i < block.getPredecessorCount(); i++) { + blockHeaderSB.append(block.getPredecessorAt(i)).append(", "); + } + + if (!blockHeaderSB.isEmpty()) { + blockHeaderSB.setLength(blockHeaderSB.length() - 2); + } + } + + if (block.getSuccessorCount() > 0) { + blockHeaderSB.append(" -> "); + for (int i = 0; i < block.getSuccessorCount(); i++) { + blockHeaderSB.append(block.getSuccessorAt(i)).append(", "); + } + + if (block.getSuccessorCount() > 0 && !blockHeaderSB.isEmpty()) { + blockHeaderSB.setLength(blockHeaderSB.length() - 2); + } + } + + out.println(blockHeaderSB); + for (var instruction : instructions.get(block)) { + out.println("\t" + instruction.toString() + " | " + instruction.getLIRInstruction().toString()); + } + out.println(); + } + } +} From 040c121cf4c7f241b5e631ba566a9905fe65735c Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 31 Jan 2026 21:28:34 +0100 Subject: [PATCH 037/112] Add base Move class --- .../verifier/ConflictedAllocationState.java | 2 +- .../verifier/FromPredecessorsResolver.java | 21 ++------ .../verifier/FromUsageResolverGlobal.java | 4 +- .../verifier/MergedBlockVerifierState.java | 11 +--- .../lir/alloc/verifier/RAVInstruction.java | 51 +++++++++++++------ .../RegisterAllocationVerifierPhase.java | 2 +- .../alloc/verifier/ValueAllocationState.java | 1 - 7 files changed, 43 insertions(+), 49 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 6f245ab36b4a..612c21c1cf7d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -55,7 +55,7 @@ public AllocationState clone() { @Override public boolean equals(AllocationState other) { - return other.isConflicted(); // TODO: handle contents + return other.isConflicted(); } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index 15fac51c9ecc..570ae9f7618c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -76,7 +76,6 @@ public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op var regTime = state.values.getKeyTime(loc); if (regTime > time) { - // TODO: replace time with priority of Moves inserted by the Register Allocator. time = regTime; // Max time blockReg = loc; } @@ -203,26 +202,14 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { Value toLocation; switch (instruction) { - case RAVInstruction.Move move -> { - fromLocation = move.from; - toLocation = move.to; - } - case RAVInstruction.Spill spill -> { - fromLocation = spill.from; - toLocation = spill.to; - } - case RAVInstruction.StackMove stack -> { - fromLocation = stack.from; - toLocation = stack.to; - } - case RAVInstruction.Reload reload -> { - fromLocation = reload.from; - toLocation = reload.to; - } case RAVInstruction.VirtualMove virtMove -> { toLocation = virtMove.location; fromLocation = null; } + case RAVInstruction.Move move -> { + fromLocation = move.from; + toLocation = move.to; + } case RAVInstruction.Op op -> { for (int i = 0; i < op.dests.count; i++) { var location = op.dests.curr[i]; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index ca3ec5300d57..ce603e3d8f5c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -108,10 +108,8 @@ public void resolvePhiFromUsage() { var instructions = blockInstructions.get(block); for (var instruction : instructions.reversed()) { switch (instruction) { + case RAVInstruction.VirtualMove ignored -> {} case RAVInstruction.Move move -> handleMove(usage, move.from, move.to); - case RAVInstruction.Spill spill -> handleMove(usage, spill.from, spill.to); - case RAVInstruction.Reload reload -> handleMove(usage, reload.from, reload.to); - case RAVInstruction.StackMove stackMove -> handleMove(usage, stackMove.from, stackMove.to); case RAVInstruction.Op op -> { if (op.lirInstruction instanceof StandardOp.LabelOp) { this.resolveLabel(usage, op, block); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 8a927a494da3..722a814b6950 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -253,17 +253,8 @@ public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstr public void update(RAVInstruction.Base instruction, BasicBlock block) { switch (instruction) { case RAVInstruction.Op op -> this.updateWithOp(op, block); - case RAVInstruction.Spill spill -> this.values.putClone(spill.to, this.values.get(spill.from)); - case RAVInstruction.Reload reload -> { - this.checkRegisterDestinationValidity(reload.to); - this.values.putClone(reload.to, this.values.get(reload.from)); - } - case RAVInstruction.Move move -> { - this.checkRegisterDestinationValidity(move.to); - this.values.putClone(move.to, this.values.get(move.from)); - } - case RAVInstruction.StackMove move -> this.values.putClone(move.to, this.values.get(move.from)); case RAVInstruction.VirtualMove virtMove -> this.updateWithVirtualMove(virtMove); + case RAVInstruction.Move move -> this.values.putClone(move.to, this.values.get(move.from)); default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 9d013386fcc9..3907959072fd 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -1,6 +1,5 @@ package jdk.graal.compiler.lir.alloc.verifier; - import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.VirtualStackSlot; @@ -9,7 +8,6 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Value; -import java.util.ArrayList; import java.util.EnumSet; import java.util.LinkedList; import java.util.List; @@ -110,7 +108,13 @@ public Value getOrig(int index) { } public void addCurrent(int index, Value value) { - assert index < this.count : "Index out of bounds"; + if (index >= this.count) { + // TestCase: DerivedOopTest + // liveBasePointers has extra item after register allocation + // TODO: handle extra items here + return; + } + this.curr[index] = value; } @@ -182,7 +186,7 @@ public Op(LIRInstruction instruction) { this.alive = new ValueArrayPair(countValuesProc.getCount()); instruction.forEachState(countValuesProc); - this.stateValues = new ValueArrayPair(countValuesProc.getCount()); + this.stateValues = new ValueArrayPair(countValuesProc.getCount()); } public boolean hasMissingDefinitions() { @@ -201,10 +205,10 @@ public String toString() { } public static class Move extends Base { - public RegisterValue from; - public RegisterValue to; + public Value from; + public Value to; - public Move(LIRInstruction instr, RegisterValue from, RegisterValue to) { + public Move(LIRInstruction instr, Value from, Value to) { super(instr); this.from = from; this.to = to; @@ -215,14 +219,29 @@ public String toString() { } } + public static class RegMove extends Move { + public RegisterValue from; + public RegisterValue to; + + public RegMove(LIRInstruction instr, RegisterValue from, RegisterValue to) { + super(instr, from, to); + this.from = from; + this.to = to; + } + + public String toString() { + return to.toString() + " = REGMOVE " + from.toString(); + } + } + // StackMove class to handle STACKMOVE instruction, temporary for now // before I decide how all Moves should be handled + all possible combinations. - public static class StackMove extends Base { + public static class StackMove extends Move { public Value from; public Value to; public StackMove(LIRInstruction instr, Value from, Value to) { - super(instr); + super(instr, from, to); assert from instanceof StackSlot || from instanceof VirtualStackSlot : "StackMove needs to receive instanceof StackSlot or VirtualStackSlot"; assert to instanceof StackSlot || to instanceof VirtualStackSlot : "StackMove needs to receive instanceof StackSlot or VirtualStackSlot"; @@ -232,16 +251,16 @@ public StackMove(LIRInstruction instr, Value from, Value to) { } public String toString() { - return to.toString() + " = MOVE " + from.toString(); + return to.toString() + " = STACKMOVE " + from.toString(); } } - public static class Reload extends Base { + public static class Reload extends Move { public Value from; public RegisterValue to; public Reload(LIRInstruction instr, RegisterValue to, Value from) { - super(instr); + super(instr, from, to); this.from = from; this.to = to; } @@ -251,12 +270,12 @@ public String toString() { } } - public static class Spill extends Base { + public static class Spill extends Move { public Value to; public RegisterValue from; public Spill(LIRInstruction instr, Value to, RegisterValue from) { - super(instr); + super(instr, from, to); this.to = to; this.from = from; } @@ -266,12 +285,12 @@ public String toString() { } } - public static class VirtualMove extends Base { + public static class VirtualMove extends Move { public Value variableOrConstant; public Value location; public VirtualMove(LIRInstruction instr, Value variableOrConstant, Value location) { - super(instr); + super(instr, variableOrConstant, location); this.variableOrConstant = variableOrConstant; this.location = location; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 3c3621d107e7..1b9a8af8a427 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -204,7 +204,7 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) } else if (result instanceof VirtualStackSlot stackSlot && input instanceof RegisterValue reg) { return new RAVInstruction.Spill(instruction, stackSlot, reg); } else if (input instanceof RegisterValue reg1 && result instanceof RegisterValue reg2) { - return new RAVInstruction.Move(instruction, reg1, reg2); + return new RAVInstruction.RegMove(instruction, reg1, reg2); } else if (input instanceof StackSlot stackSlot && result instanceof RegisterValue reg) { return new RAVInstruction.Reload(instruction, reg, stackSlot); } else if (input instanceof RegisterValue reg && result instanceof StackSlot stackSlot) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 562fa0143fff..6a30a05c3270 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -21,7 +21,6 @@ public ValueAllocationState(Value value, LIRInstruction source) { // We use variables as symbols for register validation // but real registers can also be used as that, in some cases. - // TODO: reconsider handling of StackSlots this.value = value; this.source = source; } else { From 5ed9c428a34c40caeab3b3bddbef885d863f953f Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 31 Jan 2026 21:31:20 +0100 Subject: [PATCH 038/112] Move register check to AllocationStateMap --- .../verifier/MergedAllocationStateMap.java | 33 ++++++++++++++++--- .../verifier/MergedBlockVerifierState.java | 27 +++++---------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index 0afb1ee6e9a1..988ca23437a8 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -1,7 +1,9 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; import java.util.HashMap; @@ -35,12 +37,16 @@ public class MergedAllocationStateMap { protected Map prioritizedLocations; protected int time; - public MergedAllocationStateMap() { + protected RegisterAllocationConfig registerAllocationConfig; + + public MergedAllocationStateMap(RegisterAllocationConfig registerAllocationConfig) { valueMap = new HashMap<>(); internalMap = new HashMap<>(); locationTimings = new HashMap<>(); prioritizedLocations = new HashMap<>(); time = 0; + + this.registerAllocationConfig = registerAllocationConfig; } public MergedAllocationStateMap(MergedAllocationStateMap other) { @@ -49,6 +55,8 @@ public MergedAllocationStateMap(MergedAllocationStateMap other) { locationTimings = new HashMap<>(other.locationTimings); prioritizedLocations = new HashMap<>(other.prioritizedLocations); time = other.time + 1; + + registerAllocationConfig = other.registerAllocationConfig; } public AllocationState get(Value key) { @@ -78,6 +86,11 @@ public void put(Register reg, AllocationState value) { } public void put(Value key, AllocationState state) { + this.checkRegisterDestinationValidity(key); + putWithoutRegCheck(key, state); + } + + public void putWithoutRegCheck(Value key, AllocationState state) { String keyString = this.getValueKeyString(key); locationTimings.put(keyString, time++); internalMap.put(keyString, state); @@ -119,8 +132,6 @@ public int getKeyTime(Value key) { } public Set getValueLocations(Value value) { - // TODO: maybe we can just store these in a hash map as well? - Set locations = new HashSet<>(); for (var entry : this.internalMap.entrySet()) { if (entry.getValue() instanceof ValueAllocationState valState) { @@ -140,7 +151,7 @@ public boolean mergeWith(MergedAllocationStateMap source) { if (!this.internalMap.containsKey(entry.getKey())) { changed = true; - this.put(source.valueMap.get(entry.getKey()), UnknownAllocationState.INSTANCE); + this.putWithoutRegCheck(source.valueMap.get(entry.getKey()), UnknownAllocationState.INSTANCE); } var currentValue = this.internalMap.get(entry.getKey()); @@ -149,7 +160,7 @@ public boolean mergeWith(MergedAllocationStateMap source) { changed = true; } - this.put(source.valueMap.get(entry.getKey()), result); + this.putWithoutRegCheck(source.valueMap.get(entry.getKey()), result); } return changed; @@ -164,4 +175,16 @@ protected String getValueKeyString(Value value) { return value.toString(); } + + protected void checkRegisterDestinationValidity(Value location) { + if (!ValueUtil.isRegister(location)) { + return; + } + + // Equality check so we know that this change was made by the register allocator. + var register = ValueUtil.asRegister(location); + if (!this.registerAllocationConfig.getAllocatableRegisters().contains(register)) { + throw new InvalidRegisterUsedException(register); + } + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 722a814b6950..27ec916cf87a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -24,7 +24,7 @@ public class MergedBlockVerifierState { protected ConflictResolver labelConflictResolver; public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { - this.values = new MergedAllocationStateMap(); + this.values = new MergedAllocationStateMap(registerAllocationConfig); this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; @@ -38,7 +38,7 @@ protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAlloc this.labelConflictResolver = labelConflictResolver; if (other == null) { - this.values = new MergedAllocationStateMap(); + this.values = new MergedAllocationStateMap(registerAllocationConfig); return; } @@ -259,18 +259,6 @@ public void update(RAVInstruction.Base instruction, BasicBlock block) { } } - protected void checkRegisterDestinationValidity(Value location) { - if (!ValueUtil.isRegister(location)) { - return; - } - - // Equality check so we know that this change was made by the register allocator. - var register = ValueUtil.asRegister(location); - if (!this.registerAllocationConfig.getAllocatableRegisters().contains(register)) { - throw new InvalidRegisterUsedException(register); - } - } - protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { for (int i = 0; i < op.dests.count; i++) { if (Value.ILLEGAL.equals(op.dests.orig[i])) { @@ -290,12 +278,13 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { Value location = op.dests.curr[i]; Value variable = op.dests.orig[i]; - if (!location.equals(variable)) { - // Equality check so we know that this change was made by the register allocator. - this.checkRegisterDestinationValidity(location); + if (location.equals(variable)) { + // Only check register validity if it was changed by the register allocator + // for example: rbp is used as input to start block and forbidden to be used by the allocator + this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op.lirInstruction)); + } else { + this.values.put(location, new ValueAllocationState(variable, op.lirInstruction)); } - - this.values.put(location, new ValueAllocationState(variable, op.lirInstruction)); } for (int i = 0; i < op.temp.count; i++) { From 1bfa592f933d05d6a3cf81d3da28530637dee671 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 31 Jan 2026 21:47:27 +0100 Subject: [PATCH 039/112] Add method selection logic to shared state --- .../lir/alloc/verifier/PreRegisterAllocationPhase.java | 3 +-- .../alloc/verifier/RegisterAllocationVerifierPhase.java | 5 +++-- .../verifier/RegisterAllocationVerifierPhaseState.java | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 5d0a403ae175..8aa1572845be 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -93,8 +93,7 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - var compUnitName = lirGenRes.getCompilationUnitName(); - if (state.filterStr != null && !compUnitName.contains(state.filterStr)) { + if (state.shouldBeVerified(lirGenRes)) { return; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 1b9a8af8a427..47eed115e74f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -3,6 +3,8 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.DebugContext; +import jdk.graal.compiler.debug.DebugOptions; +import jdk.graal.compiler.debug.MethodFilter; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; @@ -52,8 +54,7 @@ public RegisterAllocationVerifierPhase(RegisterAllocationVerifierPhaseState stat @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - var compUnitName = lirGenRes.getCompilationUnitName(); - if (state.filterStr != null && !compUnitName.contains(state.filterStr)) { + if (state.shouldBeVerified(lirGenRes)) { // Filter for compilation unit substring to run verification only on // certain methods, cannot use MethodFilter here because I cannot // access JavaMethod here. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java index bf621ef84c10..6ad5d7820862 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -22,6 +22,14 @@ public RegisterAllocationVerifierPhaseState(OptionValues options) { this.filterStr = RegisterAllocationVerifierPhase.Options.RAFilter.getValue(options); } + public boolean shouldBeVerified(LIRGenerationResult lirGenRes) { + var compUnitName = lirGenRes.getCompilationUnitName(); + // Filter for compilation unit substring to run verification only on + // certain methods, cannot use MethodFilter here because I cannot + // access JavaMethod here. + return filterStr == null || compUnitName.contains(filterStr); + } + public Map createInstructionMap(LIRGenerationResult lirGenRes) { this.verifierInstructions.put(lirGenRes, new IdentityHashMap<>()); return this.verifierInstructions.get(lirGenRes); From 7cf409dfcca69b674e0490d273f3a71c62c6e47a Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 14 Feb 2026 17:28:26 +0100 Subject: [PATCH 040/112] Flip logic for skipping files --- .../compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java | 2 +- .../lir/alloc/verifier/RegisterAllocationVerifierPhase.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 8aa1572845be..73b1889dd13e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -93,7 +93,7 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - if (state.shouldBeVerified(lirGenRes)) { + if (!state.shouldBeVerified(lirGenRes)) { return; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 47eed115e74f..9e58b8e59ab1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -54,7 +54,7 @@ public RegisterAllocationVerifierPhase(RegisterAllocationVerifierPhaseState stat @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - if (state.shouldBeVerified(lirGenRes)) { + if (!state.shouldBeVerified(lirGenRes)) { // Filter for compilation unit substring to run verification only on // certain methods, cannot use MethodFilter here because I cannot // access JavaMethod here. From 1659f7027ff721efe33fb4c519194f7462d8aea9 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 14 Feb 2026 17:36:11 +0100 Subject: [PATCH 041/112] Store RAVInstruction as source --- .../lir/alloc/verifier/FromPredecessorsResolver.java | 4 ++-- .../lir/alloc/verifier/LabelConflictResolver.java | 4 ++-- .../lir/alloc/verifier/MergedBlockVerifierState.java | 10 +++++----- .../lir/alloc/verifier/ValueAllocationState.java | 9 ++++++--- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index 570ae9f7618c..b38a2baa36c8 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -256,7 +256,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { variablesToBePropagated.add(variable); for (var location : locations) { if (state != null) { - state.values.put(location, new ValueAllocationState(variable, labelInstr.lirInstruction)); + state.values.put(location, new ValueAllocationState(variable, labelInstr)); } } } @@ -307,7 +307,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var variable = LIRValueUtil.asVariable(itToBePropagated.next()); var locations = variableToLocations.get(variable); for (var location : locations) { - succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr.lirInstruction)); + succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr)); } newLoc.put(variable, new VariableLocations(locations)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java index aa10d5e9d3f8..3cd45961f873 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java @@ -132,7 +132,7 @@ public ValueAllocationState resolveConflictedState(Variable target, ConflictedAl return null; } - return new ValueAllocationState(target, labelMap.get(target).getLIRInstruction()); + return new ValueAllocationState(target, labelMap.get(target)); } @Override @@ -146,6 +146,6 @@ public ValueAllocationState resolveValueState(Variable target, ValueAllocationSt return null; } - return new ValueAllocationState(target, labelMap.get(target).getLIRInstruction()); + return new ValueAllocationState(target, labelMap.get(target)); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 27ec916cf87a..717cfa233fa7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -281,9 +281,9 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { if (location.equals(variable)) { // Only check register validity if it was changed by the register allocator // for example: rbp is used as input to start block and forbidden to be used by the allocator - this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op.lirInstruction)); + this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op)); } else { - this.values.put(location, new ValueAllocationState(variable, op.lirInstruction)); + this.values.put(location, new ValueAllocationState(variable, op)); } } @@ -301,7 +301,7 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { if (virtMove.location instanceof RegisterValue) { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove.lirInstruction)); + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); } else if (LIRValueUtil.isVariable(virtMove.location)) { // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD // Move before allocation @@ -309,10 +309,10 @@ protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { // TestCase: BoxingTest.boxBoolean var locations = this.values.getValueLocations(virtMove.variableOrConstant); for (var location : locations) { - this.values.put(location, new ValueAllocationState(virtMove.location, virtMove.lirInstruction)); + this.values.put(location, new ValueAllocationState(virtMove.location, virtMove)); } } else { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove.lirInstruction)); + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 6a30a05c3270..ab9375d4d6e9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -2,7 +2,6 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.vm.ci.code.RegisterValue; @@ -11,9 +10,9 @@ public class ValueAllocationState extends AllocationState implements Cloneable { protected Value value; - protected LIRInstruction source; + protected RAVInstruction.Base source; - public ValueAllocationState(Value value, LIRInstruction source) { + public ValueAllocationState(Value value, RAVInstruction.Base source) { if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { // StackSlot, RegisterValue is present in start block in label as predefined argument // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS @@ -36,6 +35,10 @@ public Value getValue() { return value; } + public RAVInstruction.Base getSource() { + return source; + } + public AllocationState meet(AllocationState other) { if (other.isUnknown()) { return this; From 4bec15faef14466e17f98f9f4fc084d0ca354198 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 14 Feb 2026 17:39:43 +0100 Subject: [PATCH 042/112] Change LinkedList usage to ArrayList where possible --- .../lir/alloc/verifier/FromPredecessorsResolver.java | 5 +++-- .../lir/alloc/verifier/PreRegisterAllocationPhase.java | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index b38a2baa36c8..02b8acd2a350 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -9,6 +9,7 @@ import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -163,7 +164,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var locationMap = new HashMap, Map>(); var defVariableToLocations = new HashMap(); - var defBlockVariablesToPropagate = new LinkedList(); + var defBlockVariablesToPropagate = new ArrayList(); for (int i = 0; i < labelInstr.dests.count; i++) { var register = labelInstr.dests.curr[i]; var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); @@ -244,7 +245,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { } } - var variablesToBePropagated = new LinkedList(); + var variablesToBePropagated = new ArrayList(); var iterator = variablesToPropagate.iterator(); while (iterator.hasNext()) { var variable = LIRValueUtil.asVariable(iterator.next()); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 73b1889dd13e..bcf5bb1176ba 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -106,7 +105,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo BasicBlock block = lir.getBlockById(blockId); ArrayList instructions = lir.getLIRforBlock(block); - List newVars = new LinkedList<>(); + List newVars = new ArrayList<>(); constOverwriteProc.setVariableList(newVars); RAVInstruction.Base previousInstr = null; From 76c009151e947aeba94a88325258d0b8908f1594 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 14 Feb 2026 18:11:09 +0100 Subject: [PATCH 043/112] Change line endings to LF --- .../AliveConstraintViolationException.java | 50 +- .../lir/alloc/verifier/AllocationState.java | 42 +- .../verifier/CircularDefinitionError.java | 90 +-- .../lir/alloc/verifier/ConflictResolver.java | 28 +- .../verifier/ConflictedAllocationState.java | 130 ++-- ...nstantMaterializationConflictResolver.java | 268 ++++---- .../lir/alloc/verifier/DefinitionSet.java | 104 +-- .../lir/alloc/verifier/FromJumpResolver.java | 190 +++--- .../verifier/FromPredecessorsResolver.java | 646 +++++++++--------- .../lir/alloc/verifier/FromUsageResolver.java | 568 ++++++++------- .../verifier/FromUsageResolverGlobal.java | 626 ++++++++--------- .../InvalidRegisterUsedException.java | 30 +- .../verifier/KindsMismatchException.java | 64 +- .../alloc/verifier/LabelConflictResolver.java | 302 ++++---- .../alloc/verifier/LabelNotResolvedError.java | 88 +-- .../verifier/MergedAllocationStateMap.java | 380 +++++------ .../verifier/MergedBlockVerifierState.java | 642 ++++++++--------- .../alloc/verifier/MissingLocationError.java | 32 +- .../lir/alloc/verifier/PhiResolution.java | 20 +- .../verifier/PreRegisterAllocationPhase.java | 460 ++++++------- .../compiler/lir/alloc/verifier/RAVError.java | 18 +- .../lir/alloc/verifier/RAVException.java | 16 +- .../RAVFailedVerificationException.java | 44 +- .../verifier/RegisterAllocationVerifier.java | 3 - .../RegisterAllocationVerifierPhase.java | 2 - .../RegisterAllocationVerifierPhaseState.java | 98 +-- .../alloc/verifier/TaggedConstantFactory.java | 66 +- .../verifier/TaggedDataPointerConstant.java | 108 +-- .../alloc/verifier/TaggedJavaConstant.java | 174 ++--- .../TargetLocationOverwrittenException.java | 38 +- .../verifier/UnknownAllocationState.java | 60 +- .../verifier/UnknownInstructionError.java | 60 +- .../alloc/verifier/ValueAllocationState.java | 156 ++--- .../verifier/ValueNotInRegisterException.java | 102 +-- .../lir/alloc/verifier/VerifierPrinter.java | 93 ++- 35 files changed, 2893 insertions(+), 2905 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index 2136e6ecbeb8..c0c4745c6fa6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -1,25 +1,25 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.vm.ci.meta.Value; - -@SuppressWarnings("serial") -public class AliveConstraintViolationException extends RAVException { - public LIRInstruction instruction; - public BasicBlock block; - - public AliveConstraintViolationException(LIRInstruction instruction, BasicBlock block, Value location, boolean asDest) { - super(AliveConstraintViolationException.getErrorMessage(instruction, block, location, asDest)); - this.instruction = instruction; - this.block = block; - } - - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value location, boolean asDest) { - if (asDest) { - return "Location " + location + " used as both alive and output in " + instruction + " in block" + block; - } - - return "Location " + location + " used as both alive and temp in " + instruction + "in block" + block; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +@SuppressWarnings("serial") +public class AliveConstraintViolationException extends RAVException { + public LIRInstruction instruction; + public BasicBlock block; + + public AliveConstraintViolationException(LIRInstruction instruction, BasicBlock block, Value location, boolean asDest) { + super(AliveConstraintViolationException.getErrorMessage(instruction, block, location, asDest)); + this.instruction = instruction; + this.block = block; + } + + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value location, boolean asDest) { + if (asDest) { + return "Location " + location + " used as both alive and output in " + instruction + " in block" + block; + } + + return "Location " + location + " used as both alive and temp in " + instruction + "in block" + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index d786dbed438e..f0521ff2ad56 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -1,21 +1,21 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -public abstract class AllocationState { - public static AllocationState getDefault() { - return UnknownAllocationState.INSTANCE; - } - - public boolean isUnknown() { - return false; - } - - public boolean isConflicted() { - return false; - } - - public abstract AllocationState clone(); - - public abstract AllocationState meet(AllocationState other); - - public abstract boolean equals(AllocationState other); -} +package jdk.graal.compiler.lir.alloc.verifier; + +public abstract class AllocationState { + public static AllocationState getDefault() { + return UnknownAllocationState.INSTANCE; + } + + public boolean isUnknown() { + return false; + } + + public boolean isConflicted() { + return false; + } + + public abstract AllocationState clone(); + + public abstract AllocationState meet(AllocationState other); + + public abstract boolean equals(AllocationState other); +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java index e888d6aa90ff..ea3ed7fa5ed9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java @@ -1,45 +1,45 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.Variable; - -import java.util.List; - -@SuppressWarnings("serial") -public class CircularDefinitionError extends RAVError { - public CircularDefinitionError(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { - super(getErrorMessage(defBlock, predecessor, label, beingPropagated)); - } - - static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { - StringBuilder operandString = new StringBuilder("["); - var values = label.dests; - for (int i = 0; i < values.count; i++) { - if (!LIRValueUtil.isVariable(values.orig[i])) { - continue; // Avoid fatal error - } - - var variable = LIRValueUtil.asVariable(values.orig[i]); - if (!beingPropagated.contains(variable)) { - continue; - } - - var location = values.curr[i]; - - operandString.append(variable.toString()); - if (location != null) { - operandString.append(" -> ").append(location.toString()); - } else { - operandString.append(" -> ?"); - } - - operandString.append(", "); - } - - operandString.setLength(operandString.length() - 2); - operandString.append("]"); - - return "Circular definition for variable detected " + predecessor + " -> " + defBlock + " with " + operandString; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.Variable; + +import java.util.List; + +@SuppressWarnings("serial") +public class CircularDefinitionError extends RAVError { + public CircularDefinitionError(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { + super(getErrorMessage(defBlock, predecessor, label, beingPropagated)); + } + + static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { + StringBuilder operandString = new StringBuilder("["); + var values = label.dests; + for (int i = 0; i < values.count; i++) { + if (!LIRValueUtil.isVariable(values.orig[i])) { + continue; // Avoid fatal error + } + + var variable = LIRValueUtil.asVariable(values.orig[i]); + if (!beingPropagated.contains(variable)) { + continue; + } + + var location = values.curr[i]; + + operandString.append(variable.toString()); + if (location != null) { + operandString.append(" -> ").append(location.toString()); + } else { + operandString.append(" -> ?"); + } + + operandString.append(", "); + } + + operandString.setLength(operandString.length() - 2); + operandString.append("]"); + + return "Circular definition for variable detected " + predecessor + " -> " + defBlock + " with " + operandString; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index cf0558645c64..018dfce78fd0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -1,14 +1,14 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.Variable; -import jdk.vm.ci.meta.Value; - -import java.util.List; - -public interface ConflictResolver { - void prepare(LIR lir, BlockMap> blockInstructions); - ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState, Value location); - ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location); -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.meta.Value; + +import java.util.List; + +public interface ConflictResolver { + void prepare(LIR lir, BlockMap> blockInstructions); + ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState, Value location); + ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location); +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 612c21c1cf7d..3ed492c53beb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -1,65 +1,65 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public class ConflictedAllocationState extends AllocationState { - protected Set conflictedStates; - - public ConflictedAllocationState() { - this.conflictedStates = new HashSet<>(); - } - - public ConflictedAllocationState(ValueAllocationState state1, ValueAllocationState state2) { - this(); - this.conflictedStates.add(state1); // Not using addConflictedValue because a warning is thrown - this.conflictedStates.add(state2); - } - - private ConflictedAllocationState(Set conflictedStates) { - this.conflictedStates = new HashSet<>(conflictedStates); - } - - public void addConflictedValue(ValueAllocationState state) { - this.conflictedStates.add(state); - } - - public Set getConflictedStates() { - return this.conflictedStates; - } - - @Override - public boolean isConflicted() { - return true; - } - - @Override - public AllocationState meet(AllocationState other) { - var newlyConflictedState = new ConflictedAllocationState(this.getConflictedStates()); - if (other instanceof ValueAllocationState valueState) { - newlyConflictedState.addConflictedValue(valueState); - } - - if (other instanceof ConflictedAllocationState conflictedState) { - newlyConflictedState.conflictedStates.addAll(conflictedState.conflictedStates); - } - - return newlyConflictedState; - } - - @Override - public AllocationState clone() { - return new ConflictedAllocationState(this.conflictedStates); - } - - @Override - public boolean equals(AllocationState other) { - return other.isConflicted(); - } - - @Override - public String toString() { - return "Conflicted {" + Arrays.toString(this.conflictedStates.toArray()) + "}"; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class ConflictedAllocationState extends AllocationState { + protected Set conflictedStates; + + public ConflictedAllocationState() { + this.conflictedStates = new HashSet<>(); + } + + public ConflictedAllocationState(ValueAllocationState state1, ValueAllocationState state2) { + this(); + this.conflictedStates.add(state1); // Not using addConflictedValue because a warning is thrown + this.conflictedStates.add(state2); + } + + private ConflictedAllocationState(Set conflictedStates) { + this.conflictedStates = new HashSet<>(conflictedStates); + } + + public void addConflictedValue(ValueAllocationState state) { + this.conflictedStates.add(state); + } + + public Set getConflictedStates() { + return this.conflictedStates; + } + + @Override + public boolean isConflicted() { + return true; + } + + @Override + public AllocationState meet(AllocationState other) { + var newlyConflictedState = new ConflictedAllocationState(this.getConflictedStates()); + if (other instanceof ValueAllocationState valueState) { + newlyConflictedState.addConflictedValue(valueState); + } + + if (other instanceof ConflictedAllocationState conflictedState) { + newlyConflictedState.conflictedStates.addAll(conflictedState.conflictedStates); + } + + return newlyConflictedState; + } + + @Override + public AllocationState clone() { + return new ConflictedAllocationState(this.conflictedStates); + } + + @Override + public boolean equals(AllocationState other) { + return other.isConflicted(); + } + + @Override + public String toString() { + return "Conflicted {" + Arrays.toString(this.conflictedStates.toArray()) + "}"; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 29ae32b7a328..618102554ab3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -1,134 +1,134 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; -import jdk.graal.compiler.lir.VirtualStackSlot; -import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.meta.Value; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class ConstantMaterializationConflictResolver implements ConflictResolver { - protected Map constantVariableMap; - protected Set canRematerializeToStack; - - public ConstantMaterializationConflictResolver() { - this.constantVariableMap = new HashMap<>(); - this.canRematerializeToStack = new HashSet<>(); - } - - @Override - public void prepare(LIR lir, BlockMap> blockInstructions) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = blockInstructions.get(block); - - for (var instruction : instructions) { - this.prepareFromInstr(instruction, block); - } - } - } - - public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { - if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { - var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); - - // TODO: Handle moves for canRematerializeToStack - // MOVE vstack:1, const // v1 - // MOVE rax, vstack:1 - // USE v1 - - if (!LIRValueUtil.isVariable(op.dests.orig[0])) { - return; - } - - var variable = LIRValueUtil.asVariable(op.dests.orig[0]); - var constantValue = new ConstantValue(variable.getValueKind(), loadConstantOp.getConstant()); - - constantVariableMap.put(variable, constantValue); - if (loadConstantOp.canRematerializeToStack()) { - canRematerializeToStack.add(variable); - } - } - } - - @Override - public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location) { - var confStates = conflictedState.getConflictedStates(); - - Variable variable = null; - ConstantValue constantValue = null; - - for (var states : confStates) { - var value = states.getValue(); - if (LIRValueUtil.isVariable(value)) { - if (variable != null && !variable.equals(value)) { - return null; - } - - variable = LIRValueUtil.asVariable(value); - } else if (value instanceof ConstantValue constValue) { - if (constantValue != null && !constantValue.equals(constValue)) { - return null; - } - - constantValue = constValue; - } - } - - if (!target.equals(variable) || constantValue == null) { - return null; - } - - if (!this.constantVariableMap.containsKey(variable)) { - return null; - } - - if (!this.constantVariableMap.get(variable).equals(constantValue)) { - return null; - } - - if (isRematerializedToWrongLocation(variable, location)) { - throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); - } - - return new ValueAllocationState(variable, null); - } - - @Override - public ValueAllocationState resolveValueState(Variable variable, ValueAllocationState valueState, Value location) { - if (!this.constantVariableMap.containsKey(variable)) { - return null; - } - - if (valueState.getValue() instanceof ConstantValue constant) { - if (!this.constantVariableMap.get(variable).equals(constant)) { - return null; - } - - if (isRematerializedToWrongLocation(variable, location)) { - throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); - } - - return new ValueAllocationState(variable, valueState.source); - } - - return null; - } - - protected boolean isRematerializedToWrongLocation(Variable variable, Value location) { - if (location instanceof StackSlot || location instanceof VirtualStackSlot) { - return !canRematerializeToStack.contains(variable); - } - return false; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ConstantMaterializationConflictResolver implements ConflictResolver { + protected Map constantVariableMap; + protected Set canRematerializeToStack; + + public ConstantMaterializationConflictResolver() { + this.constantVariableMap = new HashMap<>(); + this.canRematerializeToStack = new HashSet<>(); + } + + @Override + public void prepare(LIR lir, BlockMap> blockInstructions) { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = blockInstructions.get(block); + + for (var instruction : instructions) { + this.prepareFromInstr(instruction, block); + } + } + } + + public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { + if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { + var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); + + // TODO: Handle moves for canRematerializeToStack + // MOVE vstack:1, const // v1 + // MOVE rax, vstack:1 + // USE v1 + + if (!LIRValueUtil.isVariable(op.dests.orig[0])) { + return; + } + + var variable = LIRValueUtil.asVariable(op.dests.orig[0]); + var constantValue = new ConstantValue(variable.getValueKind(), loadConstantOp.getConstant()); + + constantVariableMap.put(variable, constantValue); + if (loadConstantOp.canRematerializeToStack()) { + canRematerializeToStack.add(variable); + } + } + } + + @Override + public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location) { + var confStates = conflictedState.getConflictedStates(); + + Variable variable = null; + ConstantValue constantValue = null; + + for (var states : confStates) { + var value = states.getValue(); + if (LIRValueUtil.isVariable(value)) { + if (variable != null && !variable.equals(value)) { + return null; + } + + variable = LIRValueUtil.asVariable(value); + } else if (value instanceof ConstantValue constValue) { + if (constantValue != null && !constantValue.equals(constValue)) { + return null; + } + + constantValue = constValue; + } + } + + if (!target.equals(variable) || constantValue == null) { + return null; + } + + if (!this.constantVariableMap.containsKey(variable)) { + return null; + } + + if (!this.constantVariableMap.get(variable).equals(constantValue)) { + return null; + } + + if (isRematerializedToWrongLocation(variable, location)) { + throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); + } + + return new ValueAllocationState(variable, null); + } + + @Override + public ValueAllocationState resolveValueState(Variable variable, ValueAllocationState valueState, Value location) { + if (!this.constantVariableMap.containsKey(variable)) { + return null; + } + + if (valueState.getValue() instanceof ConstantValue constant) { + if (!this.constantVariableMap.get(variable).equals(constant)) { + return null; + } + + if (isRematerializedToWrongLocation(variable, location)) { + throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); + } + + return new ValueAllocationState(variable, valueState.source); + } + + return null; + } + + protected boolean isRematerializedToWrongLocation(Variable variable, Value location) { + if (location instanceof StackSlot || location instanceof VirtualStackSlot) { + return !canRematerializeToStack.contains(variable); + } + return false; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java index 76cde443f8ff..72d662386e2a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java @@ -1,52 +1,52 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.meta.Value; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class DefinitionSet { - protected Set internalSet; - protected Map valueMap; - - public DefinitionSet() { - this.internalSet = new HashSet<>(); - this.valueMap = new HashMap<>(); - } - - public DefinitionSet(DefinitionSet defSet) { - this.internalSet = new HashSet<>(defSet.internalSet); - this.valueMap = new HashMap<>(defSet.valueMap); - } - - public void add(Value value) { - if (Value.ILLEGAL.equals(value)) { - return; - } - - String valueString = this.getValueKeyString(value); - this.valueMap.put(valueString, value); - this.internalSet.add(valueString); - } - - public boolean contains(Value value) { - String valueString = this.getValueKeyString(value); - return this.internalSet.contains(valueString); - } - - protected String getValueKeyString(Value value) { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); - } - - if (LIRValueUtil.isVariable(value)) { - return "v" + LIRValueUtil.asVariable(value).index; - } - - return value.toString(); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class DefinitionSet { + protected Set internalSet; + protected Map valueMap; + + public DefinitionSet() { + this.internalSet = new HashSet<>(); + this.valueMap = new HashMap<>(); + } + + public DefinitionSet(DefinitionSet defSet) { + this.internalSet = new HashSet<>(defSet.internalSet); + this.valueMap = new HashMap<>(defSet.valueMap); + } + + public void add(Value value) { + if (Value.ILLEGAL.equals(value)) { + return; + } + + String valueString = this.getValueKeyString(value); + this.valueMap.put(valueString, value); + this.internalSet.add(valueString); + } + + public boolean contains(Value value) { + String valueString = this.getValueKeyString(value); + return this.internalSet.contains(valueString); + } + + protected String getValueKeyString(Value value) { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + if (LIRValueUtil.isVariable(value)) { + return "v" + LIRValueUtil.asVariable(value).index; + } + + return value.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java index 3c87ce263a5d..59a4cbbf6751 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java @@ -1,96 +1,94 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.vm.ci.meta.Value; - -import java.util.List; -import java.util.Set; - -public class FromJumpResolver { - public LIR lir; - public BlockMap> blockInstructions; - public BlockMap blockStates; - - public FromJumpResolver(LIR lir, BlockMap> blockInstructions, BlockMap blockStates) { - this.lir = lir; - this.blockInstructions = blockInstructions; - this.blockStates = blockStates; - } - - public void resolvePhi(BasicBlock block) { - var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getFirst(); - for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - if (state == null) { - continue; - } - - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - var inputValue = jump.alive.orig[i]; - - var varLoc = state.values.getValueLocations(inputValue); - if (locations == null) { - locations = varLoc; - continue; - } - - locations.retainAll(varLoc); - } - - if (locations == null) { - continue; - } - - Value location = null; - if (locations.size() != 1) { - if (locations.isEmpty()) { - return; - } - - for (int j = 0; j < block.getPredecessorCount(); j++) { - int time = -1; - Value blockReg = null; - for (var loc : locations) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - if (state == null) { - continue; - } - - var regTime = state.values.getKeyTime(loc); - if (regTime > time) { - time = regTime; // Max time - blockReg = loc; - } - } - - if (location == null) { - location = blockReg; - } else if (!location.equals(blockReg)) { - // Not same for all blocks, so none choosen. - return; - } - } - } else { - location = locations.stream().findFirst().get(); - } - - var registerValue = location; - // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); - - labelInstr.dests.curr[i] = registerValue; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = registerValue; - } - } - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.vm.ci.meta.Value; + +import java.util.List; +import java.util.Set; + +public class FromJumpResolver { + public LIR lir; + public BlockMap> blockInstructions; + public BlockMap blockStates; + + public FromJumpResolver(LIR lir, BlockMap> blockInstructions, BlockMap blockStates) { + this.lir = lir; + this.blockInstructions = blockInstructions; + this.blockStates = blockStates; + } + + public void resolvePhi(BasicBlock block) { + var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getFirst(); + for (int i = 0; i < labelInstr.dests.count; i++) { + Set locations = null; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + if (state == null) { + continue; + } + + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + var inputValue = jump.alive.orig[i]; + + var varLoc = state.values.getValueLocations(inputValue); + if (locations == null) { + locations = varLoc; + continue; + } + + locations.retainAll(varLoc); + } + + if (locations == null) { + continue; + } + + Value location = null; + if (locations.size() != 1) { + if (locations.isEmpty()) { + return; + } + + for (int j = 0; j < block.getPredecessorCount(); j++) { + int time = -1; + Value blockReg = null; + for (var loc : locations) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + if (state == null) { + continue; + } + + var regTime = state.values.getKeyTime(loc); + if (regTime > time) { + time = regTime; // Max time + blockReg = loc; + } + } + + if (location == null) { + location = blockReg; + } else if (!location.equals(blockReg)) { + // Not same for all blocks, so none choosen. + return; + } + } + } else { + location = locations.stream().findFirst().get(); + } + + var registerValue = location; + // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); + + labelInstr.dests.curr[i] = registerValue; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + jump.alive.curr[i] = registerValue; + } + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index 02b8acd2a350..8842f9260aad 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -1,323 +1,323 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.meta.Value; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - -public class FromPredecessorsResolver { - public LIR lir; - public BlockMap> blockInstructions; - public BlockMap blockStates; // Current states - public BlockMap blockEntryStates; // State on entry to block - - public FromPredecessorsResolver(LIR lir, BlockMap> blockInstructions, BlockMap blockStates, BlockMap blockEntryStates) { - this.lir = lir; - this.blockInstructions = blockInstructions; - this.blockStates = blockStates; - this.blockEntryStates = blockEntryStates; - } - - /** - * Fill in missing variable locations for current block's label instruction and predecessor - * jump instructions. - *

- * We are looking for intersection between locations of individual processors, this should - * give us a single register that is used for the phi, and is necessary for jump to verify - * that contents are alive that that point and for label to define where the result of phi - * will be held to used in said block. - * - * @param block Block that needs phi function output - * @param labelInstr Label instruction of said block - */ - public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { - for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - var inputValue = jump.alive.orig[i]; - - var varLoc = state.values.getValueLocations(inputValue); - if (locations == null) { - locations = varLoc; - continue; - } - - locations.retainAll(varLoc); - } - - Value location = null; - if (locations.size() != 1) { - if (locations.isEmpty()) { - return false; - } - - for (int j = 0; j < block.getPredecessorCount(); j++) { - int time = -1; - Value blockReg = null; - for (var loc : locations) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - - var regTime = state.values.getKeyTime(loc); - if (regTime > time) { - time = regTime; // Max time - blockReg = loc; - } - } - - if (location == null) { - location = blockReg; - } else if (!location.equals(blockReg)) { - // Not same for all blocks, so none choosen. - return false; - } - } - } else { - location = locations.stream().findFirst().get(); - } - - var registerValue = location; - // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); - - labelInstr.dests.curr[i] = registerValue; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = registerValue; - } - } - - return true; - } - - class VariableLocations implements Iterable { - protected Set internalList; - protected Map valueMap; - - public VariableLocations() { - this.internalList = new HashSet<>(); - this.valueMap = new HashMap<>(); - } - - public VariableLocations(VariableLocations other) { - this.internalList = new HashSet<>(other.internalList); - this.valueMap = new HashMap<>(other.valueMap); - } - - public void add(Value location) { - var locString = getValueKeyString(location); - internalList.add(locString); - valueMap.put(locString, location); - } - - public void remove(Value location) { - var locString = getValueKeyString(location); - internalList.remove(locString); - valueMap.remove(locString); - } - - public boolean isEmpty() { - return internalList.isEmpty(); - } - - public boolean contains(Value location) { - var locString = getValueKeyString(location); - return valueMap.containsKey(locString); - } - - protected String getValueKeyString(Value value) { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); - } - - return value.toString(); - } - - @Override - public Iterator iterator() { - return internalList.stream().map(valueMap::get).iterator(); - } - } - - public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { - var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); - - // Definition block needs to have this set. - var propagateMap = new HashMap, List>(); - var locationMap = new HashMap, Map>(); - - var defVariableToLocations = new HashMap(); - var defBlockVariablesToPropagate = new ArrayList(); - for (int i = 0; i < labelInstr.dests.count; i++) { - var register = labelInstr.dests.curr[i]; - var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - - defBlockVariablesToPropagate.add(variable); - - var variableLocationList = new VariableLocations(); - variableLocationList.add(register); - defVariableToLocations.put(variable, variableLocationList); - } - - Queue> worklist = new LinkedList<>(); - Set> processed = new HashSet<>(); - worklist.add(defBlock); - propagateMap.put(defBlock, defBlockVariablesToPropagate); - locationMap.put(defBlock, defVariableToLocations); - - while (!worklist.isEmpty()) { - var curr = worklist.remove(); - if (processed.contains(curr)) { - continue; - } - processed.add(curr); - - var state = this.blockStates.get(curr); - var variablesToPropagate = propagateMap.get(curr); - var variableToLocations = locationMap.get(curr); - - var instructions = blockInstructions.get(curr); - for (var instruction : instructions) { - if (curr.equals(defBlock) && instruction.lirInstruction instanceof StandardOp.LabelOp) { - continue; - } - - Value fromLocation; - Value toLocation; - - switch (instruction) { - case RAVInstruction.VirtualMove virtMove -> { - toLocation = virtMove.location; - fromLocation = null; - } - case RAVInstruction.Move move -> { - fromLocation = move.from; - toLocation = move.to; - } - case RAVInstruction.Op op -> { - for (int i = 0; i < op.dests.count; i++) { - var location = op.dests.curr[i]; - if (location == null) { - continue; - } - - var itToPropagate = variablesToPropagate.iterator(); - while (itToPropagate.hasNext()) { - var variable = LIRValueUtil.asVariable(itToPropagate.next()); - var locations = variableToLocations.get(variable); - locations.remove(location); - } - } - - continue; - } - default -> { - continue; - } - } - - var itToPropagate = variablesToPropagate.iterator(); - while (itToPropagate.hasNext()) { - var variable = LIRValueUtil.asVariable(itToPropagate.next()); - var locations = variableToLocations.get(variable); - if (fromLocation != null && locations.contains(fromLocation)) { - locations.add(toLocation); - } else if (locations.contains(toLocation)) { - locations.remove(toLocation); // Overwritten - } - } - } - - var variablesToBePropagated = new ArrayList(); - var iterator = variablesToPropagate.iterator(); - while (iterator.hasNext()) { - var variable = LIRValueUtil.asVariable(iterator.next()); - var locations = variableToLocations.get(variable); - if (locations.isEmpty()) { - continue; - } - - variablesToBePropagated.add(variable); - for (var location : locations) { - if (state != null) { - state.values.put(location, new ValueAllocationState(variable, labelInstr)); - } - } - } - - if (variablesToBePropagated.isEmpty()) { - continue; - } - - for (int i = 0; i < curr.getSuccessorCount(); i++) { - var succ = curr.getSuccessorAt(i); - var succEntryState = this.blockEntryStates.get(succ); - if (succEntryState == null) { - continue; - } - - if (succ.equals(defBlock)) { - // This means that the definition block would have same value as predecessor - // for example: B0 defines [v0] in label, B1 is it's successor as well as it's - // predecessor, and if it does not overwrite this register, it would change - // entry state for B0 to include v0, which is defined by B0. - - for (int j = 0; j < labelInstr.dests.count; j++) { - var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[j]); - - if (!variablesToPropagate.contains(variable)) { - continue; - } - - var location = labelInstr.dests.curr[j]; - var locations = variableToLocations.get(variable); - if (locations.contains(location)) { - // Only throw this error if location in label was not overwritten. - throw new CircularDefinitionError(defBlock, curr, labelInstr, variablesToBePropagated); - } - } - - continue; - } - - boolean dominates = succ.dominates(defBlock); - if (dominates) { - continue; - } - - Map newLoc = new HashMap<>(); - var itToBePropagated = variablesToBePropagated.iterator(); - while (itToBePropagated.hasNext()) { - var variable = LIRValueUtil.asVariable(itToBePropagated.next()); - var locations = variableToLocations.get(variable); - for (var location : locations) { - succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr)); - } - - newLoc.put(variable, new VariableLocations(locations)); - } - - locationMap.put(succ, newLoc); - propagateMap.put(succ, variablesToBePropagated); - worklist.add(succ); - } - } - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +public class FromPredecessorsResolver { + public LIR lir; + public BlockMap> blockInstructions; + public BlockMap blockStates; // Current states + public BlockMap blockEntryStates; // State on entry to block + + public FromPredecessorsResolver(LIR lir, BlockMap> blockInstructions, BlockMap blockStates, BlockMap blockEntryStates) { + this.lir = lir; + this.blockInstructions = blockInstructions; + this.blockStates = blockStates; + this.blockEntryStates = blockEntryStates; + } + + /** + * Fill in missing variable locations for current block's label instruction and predecessor + * jump instructions. + *

+ * We are looking for intersection between locations of individual processors, this should + * give us a single register that is used for the phi, and is necessary for jump to verify + * that contents are alive that that point and for label to define where the result of phi + * will be held to used in said block. + * + * @param block Block that needs phi function output + * @param labelInstr Label instruction of said block + */ + public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { + for (int i = 0; i < labelInstr.dests.count; i++) { + Set locations = null; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + var inputValue = jump.alive.orig[i]; + + var varLoc = state.values.getValueLocations(inputValue); + if (locations == null) { + locations = varLoc; + continue; + } + + locations.retainAll(varLoc); + } + + Value location = null; + if (locations.size() != 1) { + if (locations.isEmpty()) { + return false; + } + + for (int j = 0; j < block.getPredecessorCount(); j++) { + int time = -1; + Value blockReg = null; + for (var loc : locations) { + var pred = block.getPredecessorAt(j); + var state = this.blockStates.get(pred); + + var regTime = state.values.getKeyTime(loc); + if (regTime > time) { + time = regTime; // Max time + blockReg = loc; + } + } + + if (location == null) { + location = blockReg; + } else if (!location.equals(blockReg)) { + // Not same for all blocks, so none choosen. + return false; + } + } + } else { + location = locations.stream().findFirst().get(); + } + + var registerValue = location; + // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); + + labelInstr.dests.curr[i] = registerValue; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + jump.alive.curr[i] = registerValue; + } + } + + return true; + } + + class VariableLocations implements Iterable { + protected Set internalList; + protected Map valueMap; + + public VariableLocations() { + this.internalList = new HashSet<>(); + this.valueMap = new HashMap<>(); + } + + public VariableLocations(VariableLocations other) { + this.internalList = new HashSet<>(other.internalList); + this.valueMap = new HashMap<>(other.valueMap); + } + + public void add(Value location) { + var locString = getValueKeyString(location); + internalList.add(locString); + valueMap.put(locString, location); + } + + public void remove(Value location) { + var locString = getValueKeyString(location); + internalList.remove(locString); + valueMap.remove(locString); + } + + public boolean isEmpty() { + return internalList.isEmpty(); + } + + public boolean contains(Value location) { + var locString = getValueKeyString(location); + return valueMap.containsKey(locString); + } + + protected String getValueKeyString(Value value) { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + return value.toString(); + } + + @Override + public Iterator iterator() { + return internalList.stream().map(valueMap::get).iterator(); + } + } + + public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { + var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); + + // Definition block needs to have this set. + var propagateMap = new HashMap, List>(); + var locationMap = new HashMap, Map>(); + + var defVariableToLocations = new HashMap(); + var defBlockVariablesToPropagate = new ArrayList(); + for (int i = 0; i < labelInstr.dests.count; i++) { + var register = labelInstr.dests.curr[i]; + var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + + defBlockVariablesToPropagate.add(variable); + + var variableLocationList = new VariableLocations(); + variableLocationList.add(register); + defVariableToLocations.put(variable, variableLocationList); + } + + Queue> worklist = new LinkedList<>(); + Set> processed = new HashSet<>(); + worklist.add(defBlock); + propagateMap.put(defBlock, defBlockVariablesToPropagate); + locationMap.put(defBlock, defVariableToLocations); + + while (!worklist.isEmpty()) { + var curr = worklist.remove(); + if (processed.contains(curr)) { + continue; + } + processed.add(curr); + + var state = this.blockStates.get(curr); + var variablesToPropagate = propagateMap.get(curr); + var variableToLocations = locationMap.get(curr); + + var instructions = blockInstructions.get(curr); + for (var instruction : instructions) { + if (curr.equals(defBlock) && instruction.lirInstruction instanceof StandardOp.LabelOp) { + continue; + } + + Value fromLocation; + Value toLocation; + + switch (instruction) { + case RAVInstruction.VirtualMove virtMove -> { + toLocation = virtMove.location; + fromLocation = null; + } + case RAVInstruction.Move move -> { + fromLocation = move.from; + toLocation = move.to; + } + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + var location = op.dests.curr[i]; + if (location == null) { + continue; + } + + var itToPropagate = variablesToPropagate.iterator(); + while (itToPropagate.hasNext()) { + var variable = LIRValueUtil.asVariable(itToPropagate.next()); + var locations = variableToLocations.get(variable); + locations.remove(location); + } + } + + continue; + } + default -> { + continue; + } + } + + var itToPropagate = variablesToPropagate.iterator(); + while (itToPropagate.hasNext()) { + var variable = LIRValueUtil.asVariable(itToPropagate.next()); + var locations = variableToLocations.get(variable); + if (fromLocation != null && locations.contains(fromLocation)) { + locations.add(toLocation); + } else if (locations.contains(toLocation)) { + locations.remove(toLocation); // Overwritten + } + } + } + + var variablesToBePropagated = new ArrayList(); + var iterator = variablesToPropagate.iterator(); + while (iterator.hasNext()) { + var variable = LIRValueUtil.asVariable(iterator.next()); + var locations = variableToLocations.get(variable); + if (locations.isEmpty()) { + continue; + } + + variablesToBePropagated.add(variable); + for (var location : locations) { + if (state != null) { + state.values.put(location, new ValueAllocationState(variable, labelInstr)); + } + } + } + + if (variablesToBePropagated.isEmpty()) { + continue; + } + + for (int i = 0; i < curr.getSuccessorCount(); i++) { + var succ = curr.getSuccessorAt(i); + var succEntryState = this.blockEntryStates.get(succ); + if (succEntryState == null) { + continue; + } + + if (succ.equals(defBlock)) { + // This means that the definition block would have same value as predecessor + // for example: B0 defines [v0] in label, B1 is it's successor as well as it's + // predecessor, and if it does not overwrite this register, it would change + // entry state for B0 to include v0, which is defined by B0. + + for (int j = 0; j < labelInstr.dests.count; j++) { + var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[j]); + + if (!variablesToPropagate.contains(variable)) { + continue; + } + + var location = labelInstr.dests.curr[j]; + var locations = variableToLocations.get(variable); + if (locations.contains(location)) { + // Only throw this error if location in label was not overwritten. + throw new CircularDefinitionError(defBlock, curr, labelInstr, variablesToBePropagated); + } + } + + continue; + } + + boolean dominates = succ.dominates(defBlock); + if (dominates) { + continue; + } + + Map newLoc = new HashMap<>(); + var itToBePropagated = variablesToBePropagated.iterator(); + while (itToBePropagated.hasNext()) { + var variable = LIRValueUtil.asVariable(itToBePropagated.next()); + var locations = variableToLocations.get(variable); + for (var location : locations) { + succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr)); + } + + newLoc.put(variable, new VariableLocations(locations)); + } + + locationMap.put(succ, newLoc); + propagateMap.put(succ, variablesToBePropagated); + worklist.add(succ); + } + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java index 18039ec27a10..a54bbee87564 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -1,289 +1,279 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; -import jdk.vm.ci.meta.Value; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - -class FromUsageResolver { - protected LIR lir; - protected BlockMap> blockInstructions; - - public Map labelMap; - public Map variableRegisterMap; - public Map usageAliasMap; - - class PathEntry { - public BasicBlock block; - public PathEntry previous; - - public PathEntry(BasicBlock block) { - this.block = block; - this.previous = null; - } - - public PathEntry(BasicBlock block, PathEntry previous) { - this.block = block; - this.previous = previous; - } - - public PathEntry next(BasicBlock block) { - return new PathEntry(block, this); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o instanceof PathEntry le && le.block.equals(this.block)) { - if (le.previous == null) return this.previous == null; - return le.previous.block.equals(this.previous.block); - } - - return false; - } - - @Override - public int hashCode() { - return this.previous == null ? block.hashCode() : block.hashCode() ^ previous.block.hashCode(); - } - - @Override - public String toString() { - return (previous == null ? "" : previous.toString()) + " -> " + block.toString(); - } - } - - protected FromUsageResolver(LIR lir, BlockMap> blockInstructions) { - this.lir = lir; - this.blockInstructions = blockInstructions; - - this.labelMap = new HashMap<>(); - this.variableRegisterMap = new HashMap<>(); - this.usageAliasMap = new HashMap<>(); - } - - private Value getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVInstruction.Base usageInstruction, Value location, Variable variable) { - boolean reachedUsage = false; - while (true) { - assert path != null; - - var block = path.block; - var instructions = this.blockInstructions.get(block).reversed(); - for (var instruction : instructions) { - if (instruction == usageInstruction) { - reachedUsage = true; - continue; - } - - if (!reachedUsage) { - continue; - } - - // Tracking the value bottom up, from the usage up to the label definition - // looking for any changes to the target register that could highlight - // different register is supposed to be used, in case of reload/spill combo or - // register move. If we are wrong about the target register then, it will - // get thrown out in the verification stage. TODO: maybe try to use multiple usages to be sure? - switch (instruction) { - case RAVInstruction.Spill spill -> { - if (spill.to.equals(location)) { - location = spill.from; - } - } - case RAVInstruction.Move move -> { - if (move.to.equals(location)) { - location = move.from; - } - } - case RAVInstruction.Reload reload -> { - if (reload.to.equals(location)) { - location = reload.from; - } - } - case RAVInstruction.VirtualMove move -> { - if (move.location.equals(location) && !move.variableOrConstant.equals(variable)) { - throw new TargetLocationOverwrittenException(move, block); - } - } - // For Op, if there is a redefinition, we let the later stages handle that - default -> { - } - } - } - - if (path.block.equals(defBlock)) { - break; - } - - path = path.previous; - } - - return location; - } - - private void mapLabelVariableFromUsage( - // @formatter:off - Map> labelToBlockMap, - Variable variable, Value location, - PathEntry path, RAVInstruction.Base useInstruction) - // @formatter:on - { - var variableLabelInstr = this.labelMap.get(variable); - if (variableLabelInstr == null) { - return; - } - - var labelBlock = labelToBlockMap.get(variableLabelInstr); - var register = this.getLocationFromUsage(path, labelBlock, useInstruction, location, variable); - - this.variableRegisterMap.put(variable, register); - this.labelMap.remove(variable); - this.usageAliasMap.remove(variable); - - for (int j = 0; j < variableLabelInstr.dests.count; j++) { - if (variableLabelInstr.dests.orig[j].equals(variable)) { - variableLabelInstr.dests.curr[j] = register; - - // Need to iterate over predecessors and fill jumps as well - for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { - var pred = labelBlock.getPredecessorAt(k); - var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); - jumpOp.alive.curr[j] = register; - } - } - } - - for (var aliasEntry : this.usageAliasMap.entrySet()) { - var originalVariable = LIRValueUtil.asVariable(aliasEntry.getValue()); - if (!originalVariable.equals(variable)) { - continue; - } - - var aliasVariable = LIRValueUtil.asVariable(aliasEntry.getKey()); - var aliasLabelInstr = this.labelMap.get(aliasVariable); - if (aliasLabelInstr == null) { - continue; - } - - this.labelMap.remove(aliasVariable); - - var aliasLabelBlock = labelToBlockMap.get(aliasLabelInstr); - for (int j = 0; j < aliasLabelInstr.dests.count; j++) { - if (aliasLabelInstr.dests.orig[j].equals(aliasVariable)) { - aliasLabelInstr.dests.curr[j] = register; - - // Need to iterate over predecessors and fill jumps as well - for (int k = 0; k < aliasLabelBlock.getPredecessorCount(); k++) { - var pred = aliasLabelBlock.getPredecessorAt(k); - var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); - jumpOp.alive.curr[j] = register; - } - } - } - } - } - - private void tryMappingVariable( - // @formatter:off - Map> labelToBlockMap, Value variable, - Value location, PathEntry path, RAVInstruction.Base useInstruction - // @formatter:on - ) { - if (!LIRValueUtil.isVariable(variable)) { - return; - } - - if (LIRValueUtil.isVariable(location) || location instanceof ConstantValue) { - return; - } - - this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(variable), location, path, useInstruction); - } - - /** - * Resolves label variable registers by finding where they are used. - */ - public void resolvePhiFromUsage() { - Queue worklist = new LinkedList<>(); - worklist.add(new PathEntry(this.lir.getControlFlowGraph().getStartBlock())); - - Map> labelToBlockMap = new HashMap<>(); - - Set visited = new HashSet<>(); // Ignore already visited combinations. - while (!worklist.isEmpty()) { - var entry = worklist.poll(); - if (visited.contains(entry)) { - continue; - } - visited.add(entry); - - var block = entry.block; - var instructions = this.blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - for (int i = 0; i < labelInstr.dests.count; i++) { - if (labelInstr.dests.curr[i] == null) { - Variable variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - this.labelMap.put(variable, labelInstr); - labelToBlockMap.put(labelInstr, block); - } - } - - for (var instruction : instructions) { - switch (instruction) { - case RAVInstruction.VirtualMove move -> { - this.tryMappingVariable(labelToBlockMap, move.variableOrConstant, move.location, entry, instruction); - } - case RAVInstruction.Op op -> { - if (instruction.lirInstruction instanceof StandardOp.JumpOp) { - // Always only one successor for this jump op - // Assumption here is, that we resolve aliases with original registers immediately - // so in-case an alias was defined after that happened, it's not resolved and will fail. - var label = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); - for (int i = 0; i < op.alive.count; i++) { - if (!LIRValueUtil.isVariable(op.alive.orig[i])) { - continue; - } - - var variable = LIRValueUtil.asVariable(op.alive.orig[i]); - if (this.labelMap.get(variable) != null) { - this.usageAliasMap.put(variable, LIRValueUtil.asVariable(label.dests.orig[i])); - } - } - - continue; - } - - for (int i = 0; i < op.uses.count; i++) { - this.tryMappingVariable(labelToBlockMap, op.uses.orig[i], op.uses.curr[i], entry, instruction); - } - - for (int i = 0; i < op.alive.count; i++) { - this.tryMappingVariable(labelToBlockMap, op.alive.orig[i], op.alive.curr[i], entry, instruction); - } - } - default -> { - } - } - } - - for (int i = 0; i < block.getSuccessorCount(); i++) { - var succ = block.getSuccessorAt(i); - worklist.add(entry.next(succ)); - } - } - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +class FromUsageResolver { + protected LIR lir; + protected BlockMap> blockInstructions; + + public Map labelMap; + public Map variableRegisterMap; + public Map usageAliasMap; + + class PathEntry { + public BasicBlock block; + public PathEntry previous; + + public PathEntry(BasicBlock block) { + this.block = block; + this.previous = null; + } + + public PathEntry(BasicBlock block, PathEntry previous) { + this.block = block; + this.previous = previous; + } + + public PathEntry next(BasicBlock block) { + return new PathEntry(block, this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o instanceof PathEntry le && le.block.equals(this.block)) { + if (le.previous == null) return this.previous == null; + return le.previous.block.equals(this.previous.block); + } + + return false; + } + + @Override + public int hashCode() { + return this.previous == null ? block.hashCode() : block.hashCode() ^ previous.block.hashCode(); + } + + @Override + public String toString() { + return (previous == null ? "" : previous.toString()) + " -> " + block.toString(); + } + } + + protected FromUsageResolver(LIR lir, BlockMap> blockInstructions) { + this.lir = lir; + this.blockInstructions = blockInstructions; + + this.labelMap = new HashMap<>(); + this.variableRegisterMap = new HashMap<>(); + this.usageAliasMap = new HashMap<>(); + } + + private Value getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVInstruction.Base usageInstruction, Value location, Variable variable) { + boolean reachedUsage = false; + while (true) { + assert path != null; + + var block = path.block; + var instructions = this.blockInstructions.get(block).reversed(); + for (var instruction : instructions) { + if (instruction == usageInstruction) { + reachedUsage = true; + continue; + } + + if (!reachedUsage) { + continue; + } + + // Tracking the value bottom up, from the usage up to the label definition + // looking for any changes to the target register that could highlight + // different register is supposed to be used, in case of reload/spill combo or + // register move. If we are wrong about the target register then, it will + // get thrown out in the verification stage. TODO: maybe try to use multiple usages to be sure? + switch (instruction) { + case RAVInstruction.VirtualMove move -> { + if (move.location.equals(location) && !move.variableOrConstant.equals(variable)) { + throw new TargetLocationOverwrittenException(move, block); + } + } + case RAVInstruction.Move move -> { + if (move.to.equals(location)) { + location = move.from; + } + } + // For Op, if there is a redefinition, we let the later stages handle that + default -> { + } + } + } + + if (path.block.equals(defBlock)) { + break; + } + + path = path.previous; + } + + return location; + } + + private void mapLabelVariableFromUsage( + // @formatter:off + Map> labelToBlockMap, + Variable variable, Value location, + PathEntry path, RAVInstruction.Base useInstruction) + // @formatter:on + { + var variableLabelInstr = this.labelMap.get(variable); + if (variableLabelInstr == null) { + return; + } + + var labelBlock = labelToBlockMap.get(variableLabelInstr); + var register = this.getLocationFromUsage(path, labelBlock, useInstruction, location, variable); + + this.variableRegisterMap.put(variable, register); + this.labelMap.remove(variable); + this.usageAliasMap.remove(variable); + + for (int j = 0; j < variableLabelInstr.dests.count; j++) { + if (variableLabelInstr.dests.orig[j].equals(variable)) { + variableLabelInstr.dests.curr[j] = register; + + // Need to iterate over predecessors and fill jumps as well + for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { + var pred = labelBlock.getPredecessorAt(k); + var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); + jumpOp.alive.curr[j] = register; + } + } + } + + for (var aliasEntry : this.usageAliasMap.entrySet()) { + var originalVariable = LIRValueUtil.asVariable(aliasEntry.getValue()); + if (!originalVariable.equals(variable)) { + continue; + } + + var aliasVariable = LIRValueUtil.asVariable(aliasEntry.getKey()); + var aliasLabelInstr = this.labelMap.get(aliasVariable); + if (aliasLabelInstr == null) { + continue; + } + + this.labelMap.remove(aliasVariable); + + var aliasLabelBlock = labelToBlockMap.get(aliasLabelInstr); + for (int j = 0; j < aliasLabelInstr.dests.count; j++) { + if (aliasLabelInstr.dests.orig[j].equals(aliasVariable)) { + aliasLabelInstr.dests.curr[j] = register; + + // Need to iterate over predecessors and fill jumps as well + for (int k = 0; k < aliasLabelBlock.getPredecessorCount(); k++) { + var pred = aliasLabelBlock.getPredecessorAt(k); + var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); + jumpOp.alive.curr[j] = register; + } + } + } + } + } + + private void tryMappingVariable( + // @formatter:off + Map> labelToBlockMap, Value variable, + Value location, PathEntry path, RAVInstruction.Base useInstruction + // @formatter:on + ) { + if (!LIRValueUtil.isVariable(variable)) { + return; + } + + if (LIRValueUtil.isVariable(location) || location instanceof ConstantValue) { + return; + } + + this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(variable), location, path, useInstruction); + } + + /** + * Resolves label variable registers by finding where they are used. + */ + public void resolvePhiFromUsage() { + Queue worklist = new LinkedList<>(); + worklist.add(new PathEntry(this.lir.getControlFlowGraph().getStartBlock())); + + Map> labelToBlockMap = new HashMap<>(); + + Set visited = new HashSet<>(); // Ignore already visited combinations. + while (!worklist.isEmpty()) { + var entry = worklist.poll(); + if (visited.contains(entry)) { + continue; + } + visited.add(entry); + + var block = entry.block; + var instructions = this.blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + for (int i = 0; i < labelInstr.dests.count; i++) { + if (labelInstr.dests.curr[i] == null) { + Variable variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + this.labelMap.put(variable, labelInstr); + labelToBlockMap.put(labelInstr, block); + } + } + + for (var instruction : instructions) { + switch (instruction) { + case RAVInstruction.VirtualMove move -> { + this.tryMappingVariable(labelToBlockMap, move.variableOrConstant, move.location, entry, instruction); + } + case RAVInstruction.Op op -> { + if (instruction.lirInstruction instanceof StandardOp.JumpOp) { + // Always only one successor for this jump op + // Assumption here is, that we resolve aliases with original registers immediately + // so in-case an alias was defined after that happened, it's not resolved and will fail. + var label = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); + for (int i = 0; i < op.alive.count; i++) { + if (!LIRValueUtil.isVariable(op.alive.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(op.alive.orig[i]); + if (this.labelMap.get(variable) != null) { + this.usageAliasMap.put(variable, LIRValueUtil.asVariable(label.dests.orig[i])); + } + } + + continue; + } + + for (int i = 0; i < op.uses.count; i++) { + this.tryMappingVariable(labelToBlockMap, op.uses.orig[i], op.uses.curr[i], entry, instruction); + } + + for (int i = 0; i < op.alive.count; i++) { + this.tryMappingVariable(labelToBlockMap, op.alive.orig[i], op.alive.curr[i], entry, instruction); + } + } + default -> { + } + } + } + + for (int i = 0; i < block.getSuccessorCount(); i++) { + var succ = block.getSuccessorAt(i); + worklist.add(entry.next(succ)); + } + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index ce603e3d8f5c..5fb1595df1ec 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -1,313 +1,313 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; -import jdk.vm.ci.meta.Value; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - -/** - * Resolve label variable locations based on their first usage, - * globally - spanning over all program blocks for every variable. - */ -class FromUsageResolverGlobal { - protected LIR lir; - protected BlockMap> blockInstructions; - - public Map labelMap; - public Map defined; - public Map reached; - public Map> firstUsages; - public Map initialLocations; - public Map aliasMap; - public Map> aliasBlockMap; - public BlockMap blockUsageMap; // Entry blocks! - public List> endBlocks; - - class BlockUsage { - private final DefinitionSet reached; - private final Map locations; - - private BlockUsage() { - this.reached = new DefinitionSet(); - this.locations = new HashMap<>(); - } - - private BlockUsage(BlockUsage blockDefs) { - this.reached = new DefinitionSet(blockDefs.reached); - this.locations = new HashMap<>(blockDefs.locations); - } - - private BlockUsage merge(BlockUsage other) { - var newDefs = new BlockUsage(this); - for (var defString : other.reached.internalSet) { - var defValue = other.reached.valueMap.get(defString); - newDefs.reached.add(defValue); - } - - var iterator = other.locations.keySet().iterator(); - while (iterator.hasNext()) { - var variable = LIRValueUtil.asVariable(iterator.next()); - var defValue = other.locations.get(variable); - - if (Value.ILLEGAL.equals(defValue) && newDefs.locations.containsKey(variable)) { - continue; - } - - newDefs.locations.put(variable, defValue); - } - - return newDefs; - } - } - - protected FromUsageResolverGlobal(LIR lir, BlockMap> blockInstructions) { - this.lir = lir; - this.blockInstructions = blockInstructions; - - this.labelMap = new HashMap<>(); - this.defined = new HashMap<>(); - this.reached = new HashMap<>(); - this.firstUsages = new HashMap<>(); - this.initialLocations = new HashMap<>(); - this.aliasMap = new HashMap<>(); - this.aliasBlockMap = new HashMap<>(); - this.endBlocks = new ArrayList<>(); - - this.blockUsageMap = new BlockMap<>(lir.getControlFlowGraph()); - } - - /** - * Resolves label variable registers by finding where they are used. - */ - public void resolvePhiFromUsage() { - Queue> worklist = new LinkedList<>(); - - this.initializeUsages(); - - for (var block : endBlocks) { - blockUsageMap.put(block, new BlockUsage()); - worklist.add(block); - } - - while (!worklist.isEmpty()) { - var block = worklist.remove(); - - var usage = new BlockUsage(blockUsageMap.get(block)); - var instructions = blockInstructions.get(block); - for (var instruction : instructions.reversed()) { - switch (instruction) { - case RAVInstruction.VirtualMove ignored -> {} - case RAVInstruction.Move move -> handleMove(usage, move.from, move.to); - case RAVInstruction.Op op -> { - if (op.lirInstruction instanceof StandardOp.LabelOp) { - this.resolveLabel(usage, op, block); - continue; - } - // TODO: decide how to deal with locations being overwritten. - - if (firstUsages.containsKey(op)) { - var iterator = firstUsages.get(op).iterator(); - while (iterator.hasNext()) { - var variable = LIRValueUtil.asVariable(iterator.next()); - usage.locations.put(variable, initialLocations.get(variable)); - usage.reached.add(variable); - } - } - } - default -> { - } - } - } - - for (int i = 0; i < block.getPredecessorCount(); i++) { - var pred = block.getPredecessorAt(i); - - if (this.blockUsageMap.get(pred) == null) { - this.blockUsageMap.put(pred, new BlockUsage(usage)); - } else { - var predReached = this.blockUsageMap.get(pred); - var newReached = predReached.merge(usage); - - this.blockUsageMap.put(pred, newReached); - if (predReached.reached.internalSet.size() == newReached.reached.internalSet.size()) { - continue; - } - } - - worklist.remove(pred); - worklist.add(pred); - } - } - } - - protected void initializeUsages() { - Queue> worklist = new LinkedList<>(); - - var startBlock = this.lir.getControlFlowGraph().getStartBlock(); - worklist.add(startBlock); - - // Calculate what is defined when + usages - Set> visited = new HashSet<>(); - while (!worklist.isEmpty()) { - var block = worklist.remove(); - if (visited.contains(block)) { - continue; - } - - visited.add(block); - - var instructions = blockInstructions.get(block); - var label = (RAVInstruction.Op) instructions.getFirst(); - - for (var i = 0; i < label.dests.count; i++) { - if (LIRValueUtil.isVariable(label.dests.orig[i])) { - var variable = LIRValueUtil.asVariable(label.dests.orig[i]); - defined.put(variable, true); - labelMap.put(variable, label); - } - } - - for (var instruction : instructions) { - if (instruction instanceof RAVInstruction.Op op) { - if (op.lirInstruction instanceof StandardOp.JumpOp) { - continue; - } - - handleUsages(op.uses, op, block); - handleUsages(op.alive, op, block); - handleUsages(op.stateValues, op, block); - } - } - - var jump = (RAVInstruction.Op) instructions.getLast(); - for (var i = 0; i < jump.alive.count; i++) { - if (!LIRValueUtil.isVariable(jump.alive.orig[i])) { - continue; - } - - var variable = LIRValueUtil.asVariable(jump.alive.orig[i]); - if (defined.containsKey(variable) && !reached.containsKey(variable)) { - // No usage found before this jump - var succ = block.getSuccessorAt(0); - var succLabel = (RAVInstruction.Op) blockInstructions.get(succ).getFirst(); - - aliasBlockMap.put(variable, block); - aliasMap.put(variable, LIRValueUtil.asVariable(succLabel.dests.orig[i])); - } - } - - if (block.getSuccessorCount() == 0) { - endBlocks.add(block); - continue; - } - - for (int i = 0; i < block.getSuccessorCount(); i++) { - var succ = block.getSuccessorAt(i); - - worklist.remove(succ); - worklist.add(succ); - } - } - } - - protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { - for (var i = 0; i < values.count; i++) { - if (!LIRValueUtil.isVariable(values.orig[i])) { - continue; - } - - var variable = LIRValueUtil.asVariable(values.orig[i]); - if (defined.containsKey(variable) && !reached.containsKey(variable)) { - // Defined - variable comes from label - // Reached does not contain variable - there's no other first usage. - reached.put(variable, false); - - if (!firstUsages.containsKey(op)) { - firstUsages.put(op, new HashSet<>()); - } - - firstUsages.get(op).add(variable); - initialLocations.put(variable, values.curr[i]); - - aliasMap.remove(variable); - } - } - } - - protected void handleMove(BlockUsage usage, Value from, Value to) { - for (var entry : usage.locations.entrySet()) { - var variable = LIRValueUtil.asVariable(entry.getKey()); - var location = entry.getValue(); - if (location == null || Value.ILLEGAL.equals(location)) { - continue; - } - - if (location.equals(to) && usage.reached.contains(variable)) { - usage.locations.put(variable, from); - } - } - } - - protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBlock block) { - for (int i = 0; i < label.dests.count; i++) { - if (!LIRValueUtil.isVariable(label.dests.orig[i])) { - continue; - } - - var variable = LIRValueUtil.asVariable(label.dests.orig[i]); - if (usage.locations.get(variable) == null) { - continue; - } - - if (label.dests.curr[i] != null) { - continue; - } - - var location = usage.locations.get(variable); - if (location == null || Value.ILLEGAL.equals(location)) { - throw new IllegalStateException(); - } - - label.dests.curr[i] = location; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - - jump.alive.curr[i] = location; - } - - // Variables that are passed into jumps without any other usage are aliases - // for same variable in successor label, whenever said variable is resolved - // we now have a location for this variable and can take other moves into account. - for (var entry : aliasMap.entrySet()) { - var aliased = LIRValueUtil.asVariable(entry.getValue()); - if (variable.equals(aliased)) { - var alias = LIRValueUtil.asVariable(entry.getKey()); - var aliasBlock = aliasBlockMap.get(alias); - - if (blockUsageMap.get(aliasBlock) == null) { - this.blockUsageMap.put(aliasBlock, new BlockUsage()); - } - - var aliasBlockUsage = blockUsageMap.get(aliasBlock); - aliasBlockUsage.locations.put(alias, location); - aliasBlockUsage.reached.add(alias); - } - } - - usage.locations.put(variable, null); - } - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.meta.Value; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +/** + * Resolve label variable locations based on their first usage, + * globally - spanning over all program blocks for every variable. + */ +class FromUsageResolverGlobal { + protected LIR lir; + protected BlockMap> blockInstructions; + + public Map labelMap; + public Map defined; + public Map reached; + public Map> firstUsages; + public Map initialLocations; + public Map aliasMap; + public Map> aliasBlockMap; + public BlockMap blockUsageMap; // Entry blocks! + public List> endBlocks; + + class BlockUsage { + private final DefinitionSet reached; + private final Map locations; + + private BlockUsage() { + this.reached = new DefinitionSet(); + this.locations = new HashMap<>(); + } + + private BlockUsage(BlockUsage blockDefs) { + this.reached = new DefinitionSet(blockDefs.reached); + this.locations = new HashMap<>(blockDefs.locations); + } + + private BlockUsage merge(BlockUsage other) { + var newDefs = new BlockUsage(this); + for (var defString : other.reached.internalSet) { + var defValue = other.reached.valueMap.get(defString); + newDefs.reached.add(defValue); + } + + var iterator = other.locations.keySet().iterator(); + while (iterator.hasNext()) { + var variable = LIRValueUtil.asVariable(iterator.next()); + var defValue = other.locations.get(variable); + + if (Value.ILLEGAL.equals(defValue) && newDefs.locations.containsKey(variable)) { + continue; + } + + newDefs.locations.put(variable, defValue); + } + + return newDefs; + } + } + + protected FromUsageResolverGlobal(LIR lir, BlockMap> blockInstructions) { + this.lir = lir; + this.blockInstructions = blockInstructions; + + this.labelMap = new HashMap<>(); + this.defined = new HashMap<>(); + this.reached = new HashMap<>(); + this.firstUsages = new HashMap<>(); + this.initialLocations = new HashMap<>(); + this.aliasMap = new HashMap<>(); + this.aliasBlockMap = new HashMap<>(); + this.endBlocks = new ArrayList<>(); + + this.blockUsageMap = new BlockMap<>(lir.getControlFlowGraph()); + } + + /** + * Resolves label variable registers by finding where they are used. + */ + public void resolvePhiFromUsage() { + Queue> worklist = new LinkedList<>(); + + this.initializeUsages(); + + for (var block : endBlocks) { + blockUsageMap.put(block, new BlockUsage()); + worklist.add(block); + } + + while (!worklist.isEmpty()) { + var block = worklist.remove(); + + var usage = new BlockUsage(blockUsageMap.get(block)); + var instructions = blockInstructions.get(block); + for (var instruction : instructions.reversed()) { + switch (instruction) { + case RAVInstruction.VirtualMove ignored -> {} + case RAVInstruction.Move move -> handleMove(usage, move.from, move.to); + case RAVInstruction.Op op -> { + if (op.lirInstruction instanceof StandardOp.LabelOp) { + this.resolveLabel(usage, op, block); + continue; + } + // TODO: decide how to deal with locations being overwritten. + + if (firstUsages.containsKey(op)) { + var iterator = firstUsages.get(op).iterator(); + while (iterator.hasNext()) { + var variable = LIRValueUtil.asVariable(iterator.next()); + usage.locations.put(variable, initialLocations.get(variable)); + usage.reached.add(variable); + } + } + } + default -> { + } + } + } + + for (int i = 0; i < block.getPredecessorCount(); i++) { + var pred = block.getPredecessorAt(i); + + if (this.blockUsageMap.get(pred) == null) { + this.blockUsageMap.put(pred, new BlockUsage(usage)); + } else { + var predReached = this.blockUsageMap.get(pred); + var newReached = predReached.merge(usage); + + this.blockUsageMap.put(pred, newReached); + if (predReached.reached.internalSet.size() == newReached.reached.internalSet.size()) { + continue; + } + } + + worklist.remove(pred); + worklist.add(pred); + } + } + } + + protected void initializeUsages() { + Queue> worklist = new LinkedList<>(); + + var startBlock = this.lir.getControlFlowGraph().getStartBlock(); + worklist.add(startBlock); + + // Calculate what is defined when + usages + Set> visited = new HashSet<>(); + while (!worklist.isEmpty()) { + var block = worklist.remove(); + if (visited.contains(block)) { + continue; + } + + visited.add(block); + + var instructions = blockInstructions.get(block); + var label = (RAVInstruction.Op) instructions.getFirst(); + + for (var i = 0; i < label.dests.count; i++) { + if (LIRValueUtil.isVariable(label.dests.orig[i])) { + var variable = LIRValueUtil.asVariable(label.dests.orig[i]); + defined.put(variable, true); + labelMap.put(variable, label); + } + } + + for (var instruction : instructions) { + if (instruction instanceof RAVInstruction.Op op) { + if (op.lirInstruction instanceof StandardOp.JumpOp) { + continue; + } + + handleUsages(op.uses, op, block); + handleUsages(op.alive, op, block); + handleUsages(op.stateValues, op, block); + } + } + + var jump = (RAVInstruction.Op) instructions.getLast(); + for (var i = 0; i < jump.alive.count; i++) { + if (!LIRValueUtil.isVariable(jump.alive.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(jump.alive.orig[i]); + if (defined.containsKey(variable) && !reached.containsKey(variable)) { + // No usage found before this jump + var succ = block.getSuccessorAt(0); + var succLabel = (RAVInstruction.Op) blockInstructions.get(succ).getFirst(); + + aliasBlockMap.put(variable, block); + aliasMap.put(variable, LIRValueUtil.asVariable(succLabel.dests.orig[i])); + } + } + + if (block.getSuccessorCount() == 0) { + endBlocks.add(block); + continue; + } + + for (int i = 0; i < block.getSuccessorCount(); i++) { + var succ = block.getSuccessorAt(i); + + worklist.remove(succ); + worklist.add(succ); + } + } + } + + protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { + for (var i = 0; i < values.count; i++) { + if (!LIRValueUtil.isVariable(values.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(values.orig[i]); + if (defined.containsKey(variable) && !reached.containsKey(variable)) { + // Defined - variable comes from label + // Reached does not contain variable - there's no other first usage. + reached.put(variable, false); + + if (!firstUsages.containsKey(op)) { + firstUsages.put(op, new HashSet<>()); + } + + firstUsages.get(op).add(variable); + initialLocations.put(variable, values.curr[i]); + + aliasMap.remove(variable); + } + } + } + + protected void handleMove(BlockUsage usage, Value from, Value to) { + for (var entry : usage.locations.entrySet()) { + var variable = LIRValueUtil.asVariable(entry.getKey()); + var location = entry.getValue(); + if (location == null || Value.ILLEGAL.equals(location)) { + continue; + } + + if (location.equals(to) && usage.reached.contains(variable)) { + usage.locations.put(variable, from); + } + } + } + + protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBlock block) { + for (int i = 0; i < label.dests.count; i++) { + if (!LIRValueUtil.isVariable(label.dests.orig[i])) { + continue; + } + + var variable = LIRValueUtil.asVariable(label.dests.orig[i]); + if (usage.locations.get(variable) == null) { + continue; + } + + if (label.dests.curr[i] != null) { + continue; + } + + var location = usage.locations.get(variable); + if (location == null || Value.ILLEGAL.equals(location)) { + throw new IllegalStateException(); + } + + label.dests.curr[i] = location; + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + + jump.alive.curr[i] = location; + } + + // Variables that are passed into jumps without any other usage are aliases + // for same variable in successor label, whenever said variable is resolved + // we now have a location for this variable and can take other moves into account. + for (var entry : aliasMap.entrySet()) { + var aliased = LIRValueUtil.asVariable(entry.getValue()); + if (variable.equals(aliased)) { + var alias = LIRValueUtil.asVariable(entry.getKey()); + var aliasBlock = aliasBlockMap.get(alias); + + if (blockUsageMap.get(aliasBlock) == null) { + this.blockUsageMap.put(aliasBlock, new BlockUsage()); + } + + var aliasBlockUsage = blockUsageMap.get(aliasBlock); + aliasBlockUsage.locations.put(alias, location); + aliasBlockUsage.reached.add(alias); + } + } + + usage.locations.put(variable, null); + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java index c4446d5710ee..8f4f8fe8c300 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java @@ -1,15 +1,15 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.vm.ci.code.Register; - -@SuppressWarnings("serial") -public class InvalidRegisterUsedException extends RAVException { - - public InvalidRegisterUsedException(Register register) { - super(getErrorMessage(register)); - } - - static String getErrorMessage(Register register) { - return "Register " + register + " is not allowed to be used by RegisterAllocatorConfig"; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.vm.ci.code.Register; + +@SuppressWarnings("serial") +public class InvalidRegisterUsedException extends RAVException { + + public InvalidRegisterUsedException(Register register) { + super(getErrorMessage(register)); + } + + static String getErrorMessage(Register register) { + return "Register " + register + " is not allowed to be used by RegisterAllocatorConfig"; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java index 03e8e751b28f..8ad6ceb4c3c4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -1,32 +1,32 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.vm.ci.meta.Value; - -@SuppressWarnings("serial") -public class KindsMismatchException extends RAVException { - public LIRInstruction instruction; - public BasicBlock block; - public Value value1; - public Value value2; - public boolean origVsCurr; - - public KindsMismatchException(LIRInstruction instruction, BasicBlock block, Value value1, Value value2, boolean origVsCurr) { - super(KindsMismatchException.getErrorMessage(instruction, block, value1, value2, origVsCurr)); - - this.instruction = instruction; - this.block = block; - this.value1 = value1; - this.value2 = value2; - this.origVsCurr = origVsCurr; - } - - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value value1, Value value2, boolean origVsCurr) { - if (origVsCurr) { - return value1.getValueKind() + " has different kind after allocation: " + value2.getValueKind() + " in " + instruction + " in block " + block; - } - - return "Value in location has different kind: " + value1.getValueKind() + " vs. " + value2.getValueKind() + " in " + instruction + " in block " + block; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +@SuppressWarnings("serial") +public class KindsMismatchException extends RAVException { + public LIRInstruction instruction; + public BasicBlock block; + public Value value1; + public Value value2; + public boolean origVsCurr; + + public KindsMismatchException(LIRInstruction instruction, BasicBlock block, Value value1, Value value2, boolean origVsCurr) { + super(KindsMismatchException.getErrorMessage(instruction, block, value1, value2, origVsCurr)); + + this.instruction = instruction; + this.block = block; + this.value1 = value1; + this.value2 = value2; + this.origVsCurr = origVsCurr; + } + + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value value1, Value value2, boolean origVsCurr) { + if (origVsCurr) { + return value1.getValueKind() + " has different kind after allocation: " + value2.getValueKind() + " in " + instruction + " in block " + block; + } + + return "Value in location has different kind: " + value1.getValueKind() + " vs. " + value2.getValueKind() + " in " + instruction + " in block " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java index 3cd45961f873..0451a2792080 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java @@ -1,151 +1,151 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.Variable; -import jdk.vm.ci.meta.Value; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class LabelConflictResolver implements ConflictResolver { - protected LIR lir; - protected BlockMap> blockInstructions; - - protected Map labelMap; - protected Map> rules; - protected Map> expandedRules; - - public LabelConflictResolver() { - this.labelMap = new HashMap<>(); - this.rules = new HashMap<>(); - this.expandedRules = new HashMap<>(); - } - - @Override - public void prepare(LIR lir, BlockMap> blockInstructions) { - this.lir = lir; - this.blockInstructions = blockInstructions; - - this.buildLabelMap(); - this.buildRules(); - this.expandRules(); - } - - protected void buildLabelMap() { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - - for (int i = 0; i < labelInstr.dests.count; i++) { - if (!LIRValueUtil.isVariable(labelInstr.dests.orig[i])) { - continue; - } - - var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - labelMap.put(labelVariable, labelInstr); - } - } - } - - protected void buildRules() { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - - for (int i = 0; i < labelInstr.dests.count; i++) { - if (!LIRValueUtil.isVariable(labelInstr.dests.orig[i])) { - continue; - } - - var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - Set resolutions = new HashSet<>(); - - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var predInstructions = blockInstructions.get(pred); - var jumpInstr = (RAVInstruction.Op) predInstructions.getLast(); - - resolutions.add(jumpInstr.alive.orig[i]); - } - - rules.put(labelVariable, resolutions); - } - } - } - - protected void expandRules() { - for (var entry : rules.entrySet()) { - var variable = LIRValueUtil.asVariable(entry.getKey()); - - ArrayList sources = new ArrayList<>(entry.getValue()); - int sourceCount = sources.size(); - for (int i = 0; i < sourceCount; i++) { - var source = sources.get(i); - if (!LIRValueUtil.isVariable(source)) { - continue; - } - - var sourceVariable = LIRValueUtil.asVariable(source); - if (rules.containsKey(sourceVariable)) { - for (var newSource : rules.get(sourceVariable)) { - if (sources.contains(newSource)) { - continue; - } - - sources.add(newSource); - sourceCount++; - } - } - } - - expandedRules.put(variable, new HashSet<>(sources)); - } - } - - protected boolean isSubsetOfValues(Set superset, Set subset) { - for (var v : subset) { - if (!superset.contains(v.getValue())) { - return false; - } - } - return true; - } - - @Override - public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location) { - if (!this.expandedRules.containsKey(target)) { - return null; - } - - var ruleSet = this.expandedRules.get(target); - var confStates = conflictedState.getConflictedStates(); - - if (!this.isSubsetOfValues(ruleSet, confStates)) { - return null; - } - - return new ValueAllocationState(target, labelMap.get(target)); - } - - @Override - public ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState, Value location) { - if (!this.expandedRules.containsKey(target)) { - return null; - } - - var ruleSet = this.expandedRules.get(target); - if (!ruleSet.contains(valueState.getValue())) { - return null; - } - - return new ValueAllocationState(target, labelMap.get(target)); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.Variable; +import jdk.vm.ci.meta.Value; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class LabelConflictResolver implements ConflictResolver { + protected LIR lir; + protected BlockMap> blockInstructions; + + protected Map labelMap; + protected Map> rules; + protected Map> expandedRules; + + public LabelConflictResolver() { + this.labelMap = new HashMap<>(); + this.rules = new HashMap<>(); + this.expandedRules = new HashMap<>(); + } + + @Override + public void prepare(LIR lir, BlockMap> blockInstructions) { + this.lir = lir; + this.blockInstructions = blockInstructions; + + this.buildLabelMap(); + this.buildRules(); + this.expandRules(); + } + + protected void buildLabelMap() { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + + for (int i = 0; i < labelInstr.dests.count; i++) { + if (!LIRValueUtil.isVariable(labelInstr.dests.orig[i])) { + continue; + } + + var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + labelMap.put(labelVariable, labelInstr); + } + } + } + + protected void buildRules() { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = blockInstructions.get(block); + var labelInstr = (RAVInstruction.Op) instructions.getFirst(); + + for (int i = 0; i < labelInstr.dests.count; i++) { + if (!LIRValueUtil.isVariable(labelInstr.dests.orig[i])) { + continue; + } + + var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + Set resolutions = new HashSet<>(); + + for (int j = 0; j < block.getPredecessorCount(); j++) { + var pred = block.getPredecessorAt(j); + var predInstructions = blockInstructions.get(pred); + var jumpInstr = (RAVInstruction.Op) predInstructions.getLast(); + + resolutions.add(jumpInstr.alive.orig[i]); + } + + rules.put(labelVariable, resolutions); + } + } + } + + protected void expandRules() { + for (var entry : rules.entrySet()) { + var variable = LIRValueUtil.asVariable(entry.getKey()); + + ArrayList sources = new ArrayList<>(entry.getValue()); + int sourceCount = sources.size(); + for (int i = 0; i < sourceCount; i++) { + var source = sources.get(i); + if (!LIRValueUtil.isVariable(source)) { + continue; + } + + var sourceVariable = LIRValueUtil.asVariable(source); + if (rules.containsKey(sourceVariable)) { + for (var newSource : rules.get(sourceVariable)) { + if (sources.contains(newSource)) { + continue; + } + + sources.add(newSource); + sourceCount++; + } + } + } + + expandedRules.put(variable, new HashSet<>(sources)); + } + } + + protected boolean isSubsetOfValues(Set superset, Set subset) { + for (var v : subset) { + if (!superset.contains(v.getValue())) { + return false; + } + } + return true; + } + + @Override + public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location) { + if (!this.expandedRules.containsKey(target)) { + return null; + } + + var ruleSet = this.expandedRules.get(target); + var confStates = conflictedState.getConflictedStates(); + + if (!this.isSubsetOfValues(ruleSet, confStates)) { + return null; + } + + return new ValueAllocationState(target, labelMap.get(target)); + } + + @Override + public ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState, Value location) { + if (!this.expandedRules.containsKey(target)) { + return null; + } + + var ruleSet = this.expandedRules.get(target); + if (!ruleSet.contains(valueState.getValue())) { + return null; + } + + return new ValueAllocationState(target, labelMap.get(target)); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java index 9c05fd638d91..d595fe00d194 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java @@ -1,44 +1,44 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; - -@SuppressWarnings("serial") -public class LabelNotResolvedError extends RAVError { - public BasicBlock block; - public RAVInstruction.Op label; - public PhiResolution resolution; - - public LabelNotResolvedError(BasicBlock block, RAVInstruction.Op label, PhiResolution resolution) { - super(LabelNotResolvedError.getErrorMessage(label)); - - this.block = block; - this.label = label; - this.resolution = resolution; - } - - static String getErrorMessage(RAVInstruction.Op label) { - StringBuilder labelStringBuilder = new StringBuilder("["); - StringBuilder unresolvedVariablesStringBuilder = new StringBuilder(); - for (int i = 0; i < label.dests.count; i++) { - var variable = label.dests.orig[i]; - var location = label.dests.curr[i]; - - labelStringBuilder.append(variable.toString()); - if (location != null) { - labelStringBuilder.append(" -> ").append(location); - } else { - labelStringBuilder.append(" -> ?"); - - unresolvedVariablesStringBuilder.append(variable); - unresolvedVariablesStringBuilder.append(", "); - } - - labelStringBuilder.append(", "); - } - - int unresLen = unresolvedVariablesStringBuilder.length(); - unresolvedVariablesStringBuilder.delete(unresLen - 2, unresLen); - - return "Could not resolve " + unresolvedVariablesStringBuilder + ": LABEL " + labelStringBuilder + "]"; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; + +@SuppressWarnings("serial") +public class LabelNotResolvedError extends RAVError { + public BasicBlock block; + public RAVInstruction.Op label; + public PhiResolution resolution; + + public LabelNotResolvedError(BasicBlock block, RAVInstruction.Op label, PhiResolution resolution) { + super(LabelNotResolvedError.getErrorMessage(label)); + + this.block = block; + this.label = label; + this.resolution = resolution; + } + + static String getErrorMessage(RAVInstruction.Op label) { + StringBuilder labelStringBuilder = new StringBuilder("["); + StringBuilder unresolvedVariablesStringBuilder = new StringBuilder(); + for (int i = 0; i < label.dests.count; i++) { + var variable = label.dests.orig[i]; + var location = label.dests.curr[i]; + + labelStringBuilder.append(variable.toString()); + if (location != null) { + labelStringBuilder.append(" -> ").append(location); + } else { + labelStringBuilder.append(" -> ?"); + + unresolvedVariablesStringBuilder.append(variable); + unresolvedVariablesStringBuilder.append(", "); + } + + labelStringBuilder.append(", "); + } + + int unresLen = unresolvedVariablesStringBuilder.length(); + unresolvedVariablesStringBuilder.delete(unresLen - 2, unresLen); + + return "Could not resolve " + unresolvedVariablesStringBuilder + ": LABEL " + labelStringBuilder + "]"; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index 988ca23437a8..275ddc206e78 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -1,190 +1,190 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; -import jdk.vm.ci.code.Register; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.ValueUtil; -import jdk.vm.ci.meta.Value; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class MergedAllocationStateMap { - /** - * These are instances of Value we need to keep for getValueLocations, - * indexed by their string representation. - *

- * Because Values have their own toString implementation, - * that the hash map uses and for some Values we do not - * want this (especially due to kinds), which are irrelevant - * for location indices, we have a getValueKeyString which - * does what we need. - */ - protected Map valueMap; - protected Map internalMap; - protected Map locationTimings; - /** - * Prioritized locations are ones made by the register allocator itself. - *

- * Whenever we are resolving phi variables, these are prioritized - * because they are likely what the register allocator chose - * to be used as phi locations. - *

- * If there's multiple, then time should make the difference. - */ - protected Map prioritizedLocations; - protected int time; - - protected RegisterAllocationConfig registerAllocationConfig; - - public MergedAllocationStateMap(RegisterAllocationConfig registerAllocationConfig) { - valueMap = new HashMap<>(); - internalMap = new HashMap<>(); - locationTimings = new HashMap<>(); - prioritizedLocations = new HashMap<>(); - time = 0; - - this.registerAllocationConfig = registerAllocationConfig; - } - - public MergedAllocationStateMap(MergedAllocationStateMap other) { - valueMap = new HashMap<>(other.valueMap); - internalMap = new HashMap<>(other.internalMap); - locationTimings = new HashMap<>(other.locationTimings); - prioritizedLocations = new HashMap<>(other.prioritizedLocations); - time = other.time + 1; - - registerAllocationConfig = other.registerAllocationConfig; - } - - public AllocationState get(Value key) { - return this.get(key, AllocationState.getDefault()); - } - - public AllocationState get(Value key, AllocationState defaultValue) { - String keyString = this.getValueKeyString(key); - var state = internalMap.get(keyString); - if (state == null) { - return defaultValue; - } - return state; - } - - public AllocationState get(Register register) { - return this.get(register.asValue()); - } - - public void putAsPrioritized(Value key, AllocationState value) { - this.put(key, value); - this.prioritizedLocations.put(this.getValueKeyString(key), true); - } - - public void put(Register reg, AllocationState value) { - this.put(reg.asValue(), value); - } - - public void put(Value key, AllocationState state) { - this.checkRegisterDestinationValidity(key); - putWithoutRegCheck(key, state); - } - - public void putWithoutRegCheck(Value key, AllocationState state) { - String keyString = this.getValueKeyString(key); - locationTimings.put(keyString, time++); - internalMap.put(keyString, state); - valueMap.put(keyString, key); - - if (prioritizedLocations.containsKey(keyString)) { - prioritizedLocations.put(keyString, false); - } - } - - public void putClone(Value key, AllocationState value) { - if (value.isUnknown()) { - this.put(key, value); - return; - } - - this.put(key, value.clone()); - } - - public void putClone(Register reg, AllocationState value) { - this.put(reg.asValue(), value.clone()); - } - - public void putCloneAsPrioritized(Value key, AllocationState value) { - this.putClone(key, value); - this.prioritizedLocations.put(this.getValueKeyString(key), true); - } - - public boolean isPrioritized(Value key) { - String keyString = this.getValueKeyString(key); - return prioritizedLocations.containsKey(keyString); - } - - public int getKeyTime(Value key) { - String keyString = this.getValueKeyString(key); - var time = locationTimings.get(keyString); - assert time != null : "Time for key " + keyString + " not present."; - return time; - } - - public Set getValueLocations(Value value) { - Set locations = new HashSet<>(); - for (var entry : this.internalMap.entrySet()) { - if (entry.getValue() instanceof ValueAllocationState valState) { - if (valState.getValue().equals(value)) { - var location = this.valueMap.get(entry.getKey()); - assert location != null : "Value not present in ValueMap: " + entry.getKey(); - locations.add(location); - } - } - } - return locations; - } - - public boolean mergeWith(MergedAllocationStateMap source) { - boolean changed = false; - for (var entry : source.internalMap.entrySet()) { - if (!this.internalMap.containsKey(entry.getKey())) { - changed = true; - - this.putWithoutRegCheck(source.valueMap.get(entry.getKey()), UnknownAllocationState.INSTANCE); - } - - var currentValue = this.internalMap.get(entry.getKey()); - var result = this.internalMap.get(entry.getKey()).meet(entry.getValue()); - if (!currentValue.equals(result)) { - changed = true; - } - - this.putWithoutRegCheck(source.valueMap.get(entry.getKey()), result); - } - - return changed; - } - - protected String getValueKeyString(Value value) { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); - } - - assert !Value.ILLEGAL.equals(value) : "Cannot use ILLEGAL as key in AllocationStateMap"; - - return value.toString(); - } - - protected void checkRegisterDestinationValidity(Value location) { - if (!ValueUtil.isRegister(location)) { - return; - } - - // Equality check so we know that this change was made by the register allocator. - var register = ValueUtil.asRegister(location); - if (!this.registerAllocationConfig.getAllocatableRegisters().contains(register)) { - throw new InvalidRegisterUsedException(register); - } - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.Value; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class MergedAllocationStateMap { + /** + * These are instances of Value we need to keep for getValueLocations, + * indexed by their string representation. + *

+ * Because Values have their own toString implementation, + * that the hash map uses and for some Values we do not + * want this (especially due to kinds), which are irrelevant + * for location indices, we have a getValueKeyString which + * does what we need. + */ + protected Map valueMap; + protected Map internalMap; + protected Map locationTimings; + /** + * Prioritized locations are ones made by the register allocator itself. + *

+ * Whenever we are resolving phi variables, these are prioritized + * because they are likely what the register allocator chose + * to be used as phi locations. + *

+ * If there's multiple, then time should make the difference. + */ + protected Map prioritizedLocations; + protected int time; + + protected RegisterAllocationConfig registerAllocationConfig; + + public MergedAllocationStateMap(RegisterAllocationConfig registerAllocationConfig) { + valueMap = new HashMap<>(); + internalMap = new HashMap<>(); + locationTimings = new HashMap<>(); + prioritizedLocations = new HashMap<>(); + time = 0; + + this.registerAllocationConfig = registerAllocationConfig; + } + + public MergedAllocationStateMap(MergedAllocationStateMap other) { + valueMap = new HashMap<>(other.valueMap); + internalMap = new HashMap<>(other.internalMap); + locationTimings = new HashMap<>(other.locationTimings); + prioritizedLocations = new HashMap<>(other.prioritizedLocations); + time = other.time + 1; + + registerAllocationConfig = other.registerAllocationConfig; + } + + public AllocationState get(Value key) { + return this.get(key, AllocationState.getDefault()); + } + + public AllocationState get(Value key, AllocationState defaultValue) { + String keyString = this.getValueKeyString(key); + var state = internalMap.get(keyString); + if (state == null) { + return defaultValue; + } + return state; + } + + public AllocationState get(Register register) { + return this.get(register.asValue()); + } + + public void putAsPrioritized(Value key, AllocationState value) { + this.put(key, value); + this.prioritizedLocations.put(this.getValueKeyString(key), true); + } + + public void put(Register reg, AllocationState value) { + this.put(reg.asValue(), value); + } + + public void put(Value key, AllocationState state) { + this.checkRegisterDestinationValidity(key); + putWithoutRegCheck(key, state); + } + + public void putWithoutRegCheck(Value key, AllocationState state) { + String keyString = this.getValueKeyString(key); + locationTimings.put(keyString, time++); + internalMap.put(keyString, state); + valueMap.put(keyString, key); + + if (prioritizedLocations.containsKey(keyString)) { + prioritizedLocations.put(keyString, false); + } + } + + public void putClone(Value key, AllocationState value) { + if (value.isUnknown()) { + this.put(key, value); + return; + } + + this.put(key, value.clone()); + } + + public void putClone(Register reg, AllocationState value) { + this.put(reg.asValue(), value.clone()); + } + + public void putCloneAsPrioritized(Value key, AllocationState value) { + this.putClone(key, value); + this.prioritizedLocations.put(this.getValueKeyString(key), true); + } + + public boolean isPrioritized(Value key) { + String keyString = this.getValueKeyString(key); + return prioritizedLocations.containsKey(keyString); + } + + public int getKeyTime(Value key) { + String keyString = this.getValueKeyString(key); + var time = locationTimings.get(keyString); + assert time != null : "Time for key " + keyString + " not present."; + return time; + } + + public Set getValueLocations(Value value) { + Set locations = new HashSet<>(); + for (var entry : this.internalMap.entrySet()) { + if (entry.getValue() instanceof ValueAllocationState valState) { + if (valState.getValue().equals(value)) { + var location = this.valueMap.get(entry.getKey()); + assert location != null : "Value not present in ValueMap: " + entry.getKey(); + locations.add(location); + } + } + } + return locations; + } + + public boolean mergeWith(MergedAllocationStateMap source) { + boolean changed = false; + for (var entry : source.internalMap.entrySet()) { + if (!this.internalMap.containsKey(entry.getKey())) { + changed = true; + + this.putWithoutRegCheck(source.valueMap.get(entry.getKey()), UnknownAllocationState.INSTANCE); + } + + var currentValue = this.internalMap.get(entry.getKey()); + var result = this.internalMap.get(entry.getKey()).meet(entry.getValue()); + if (!currentValue.equals(result)) { + changed = true; + } + + this.putWithoutRegCheck(source.valueMap.get(entry.getKey()), result); + } + + return changed; + } + + protected String getValueKeyString(Value value) { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + assert !Value.ILLEGAL.equals(value) : "Cannot use ILLEGAL as key in AllocationStateMap"; + + return value.toString(); + } + + protected void checkRegisterDestinationValidity(Value location) { + if (!ValueUtil.isRegister(location)) { + return; + } + + // Equality check so we know that this change was made by the register allocator. + var register = ValueUtil.asRegister(location); + if (!this.registerAllocationConfig.getAllocatableRegisters().contains(register)) { + throw new InvalidRegisterUsedException(register); + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 717cfa233fa7..e71ee369f55d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -1,318 +1,324 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.LIRKindWithCast; -import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.CastValue; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.ValueUtil; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.Value; -import jdk.vm.ci.meta.ValueKind; - -public class MergedBlockVerifierState { - public MergedAllocationStateMap values; - - protected PhiResolution phiResolution; - protected RegisterAllocationConfig registerAllocationConfig; - - protected ConflictResolver conflictConstantResolver; - protected ConflictResolver labelConflictResolver; - - public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { - this.values = new MergedAllocationStateMap(registerAllocationConfig); - this.phiResolution = phiResolution; - this.registerAllocationConfig = registerAllocationConfig; - this.conflictConstantResolver = constantConflictResolver; - this.labelConflictResolver = labelConflictResolver; - } - - protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { - this.phiResolution = phiResolution; - this.registerAllocationConfig = registerAllocationConfig; - this.conflictConstantResolver = constantConflictResolver; - this.labelConflictResolver = labelConflictResolver; - - if (other == null) { - this.values = new MergedAllocationStateMap(registerAllocationConfig); - return; - } - - this.values = new MergedAllocationStateMap(other.values); - } - - public MergedAllocationStateMap getValues() { - return values; - } - - public boolean meetWith(MergedBlockVerifierState other) { - return this.values.mergeWith(other.getValues()); - } - - protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block, RAVInstruction.Op labelOp) { - // Check that incoming values are not unknown or conflicted - these only matter if used - for (int idx = 0; idx < values.count; idx++) { - var orig = values.orig[idx]; - var curr = values.curr[idx]; - - assert orig != null; - - boolean isJump = op.lirInstruction instanceof StandardOp.JumpOp; - if (curr == null) { - if (isJump) { - if (phiResolution == PhiResolution.FromUsage || phiResolution == PhiResolution.FromUsageGlobal) { - // Variable has no usage, thus no location present. - continue; - } - - if (phiResolution == PhiResolution.ByAllocator || phiResolution == PhiResolution.FromConflicts) { - continue; - } - - throw new LabelNotResolvedError(block, labelOp, phiResolution); - } - - throw new MissingLocationError(op.lirInstruction, block, orig); - } - - if (orig.equals(curr)) { - // In this case nothing has changed so we have nothing to verify - continue; - } - - if (!kindsEqual(orig, curr) && !isJump) { - throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); - } - - AllocationState state = this.values.get(curr); - if (state.isUnknown()) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); - } - - if (state.isConflicted()) { - var variable = LIRValueUtil.asVariable(orig); - var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); - - if (phiResolution == PhiResolution.FromConflicts && resolvedState == null) { - resolvedState = this.labelConflictResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); - if (resolvedState == null) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); - } - } - - this.values.put(curr, resolvedState); - continue; - } - - if (state instanceof ValueAllocationState valAllocState) { - if (!kindsEqualFromState(orig, valAllocState.value)) { - throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); - } - - if (!valAllocState.value.equals(orig)) { - if (orig instanceof CastValue castValue && valAllocState.value.equals(castValue.underlyingValue())) { - continue; // They aren't equal here because of the CastValue, so if they are equal afterwards, we skip next part. - } - - if (valAllocState.value instanceof ConstantValue) { - var variable = LIRValueUtil.asVariable(orig); - var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); - if (resolvedState != null) { - this.values.put(curr, resolvedState); - continue; - } - } - - if (phiResolution == PhiResolution.FromConflicts && LIRValueUtil.isVariable(orig)) { - var variable = LIRValueUtil.asVariable(orig); - var resolvedState = labelConflictResolver.resolveValueState(variable, valAllocState, curr); - if (resolvedState != null) { - this.values.put(curr, resolvedState); - continue; - } - } - - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); - } - - continue; - } - - throw GraalError.shouldNotReachHere("Invalid state " + state); - } - } - - protected boolean kindsEqual(Value orig, Value curr) { - var origKind = orig.getValueKind(); - var currKind = curr.getValueKind(); - - if (currKind.equals(origKind)) { - return true; - } - - if (origKind instanceof LIRKindWithCast || currKind instanceof LIRKindWithCast) { - // TestCase: BoxingTest.boxShort - // MOV (x: [v11|QWORD[.] + 12], y: reinterpret: v0|DWORD as: WORD) size: WORD - // MOV (x: [rax|QWORD[.] + 12], y: r10|WORD(DWORD)) size: WORD - return origKind.getPlatformKind().equals(currKind.getPlatformKind()); - } - - // TODO: maybe we should also look at the type before cast - // LIRKindWithCast.getActualKind() - // CastValue.underlyingValue().getValueKind() - return false; - } - - protected boolean kindsEqualFromState(Value orig, Value fromState) { - ValueKind origKind = orig.getValueKind(); - ValueKind currKind = fromState.getValueKind(); - if (orig instanceof CastValue castOrig) { - origKind = castOrig.underlyingValue().getValueKind(); - } - - return origKind.equals(currKind); - } - - protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock block) { - for (int i = 0; i < instruction.alive.count; i++) { - Value value = instruction.alive.curr[i]; - if (Value.ILLEGAL.equals(value)) { - continue; // TODO: remove IllegalValues from these arrays. - } - - for (int j = 0; j < instruction.temp.count; j++) { - if (value.equals(instruction.temp.curr[j])) { - throw new AliveConstraintViolationException(instruction.lirInstruction, block, value, false); - } - } - - for (int j = 0; j < instruction.dests.count; j++) { - if (value.equals(instruction.dests.curr[j])) { - throw new AliveConstraintViolationException(instruction.lirInstruction, block, value, true); - } - } - } - } - - public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstruction.Op labelOp) { - if (instruction instanceof RAVInstruction.Op op) { - checkInputs(op.uses, op, block, labelOp); - checkInputs(op.alive, op, block, labelOp); - - for (int i = 0; i < op.temp.count; i++) { - var curr = op.temp.curr[i]; - var orig = op.temp.orig[i]; - - if (!kindsEqual(orig, curr)) { - // Make sure the assigned register has the correct kind for temp. - throw new KindsMismatchException(instruction.lirInstruction, block, orig, curr, true); - } - } - - this.checkInputs(op.stateValues, op, block, labelOp); - - int kindIdx = 0; - for (int i = 0; i < op.stateValues.count; i++) { - var orig = op.stateValues.orig[i]; - var curr = op.stateValues.curr[i]; - - var state = this.values.get(curr); - if (state instanceof ValueAllocationState valueAllocationState && valueAllocationState.getValue().equals(orig)) { - continue; - } - - JavaKind kind = null; - while (kindIdx < op.kinds.length) { - JavaKind target = op.kinds[kindIdx++]; - if (!JavaKind.Illegal.equals(target)) { - kind = target; - break; - } - // Illegal values are ignored when iterating over state values - // but kept in the kinds array so we need to skip them. - } - - if (!JavaKind.Object.equals(kind)) { - continue; - } - - // TODO: how to handle object kind? - // If the same virtual value is present in the register then there isn't anything to be done - // but maybe if it was changed we also maybe want to make sure that there's a pointer (Object) present? - // but for that we would need to track that information based on GC instructions - } - - this.checkAliveConstraint(op, block); - } - } - - public void update(RAVInstruction.Base instruction, BasicBlock block) { - switch (instruction) { - case RAVInstruction.Op op -> this.updateWithOp(op, block); - case RAVInstruction.VirtualMove virtMove -> this.updateWithVirtualMove(virtMove); - case RAVInstruction.Move move -> this.values.putClone(move.to, this.values.get(move.from)); - default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); - } - } - - protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { - for (int i = 0; i < op.dests.count; i++) { - if (Value.ILLEGAL.equals(op.dests.orig[i])) { - continue; // Safe to ignore, when destination is illegal value, not when used. - } - - assert op.dests.orig[i] != null; - - if (op.dests.curr[i] == null) { - if (phiResolution == PhiResolution.FromJump) { - throw new LabelNotResolvedError(block, op, phiResolution); - } - - continue; - } - - Value location = op.dests.curr[i]; - Value variable = op.dests.orig[i]; - - if (location.equals(variable)) { - // Only check register validity if it was changed by the register allocator - // for example: rbp is used as input to start block and forbidden to be used by the allocator - this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op)); - } else { - this.values.put(location, new ValueAllocationState(variable, op)); - } - } - - for (int i = 0; i < op.temp.count; i++) { - var value = op.temp.curr[i]; - if (Value.ILLEGAL.equals(value)) { - continue; - } - - // We cannot believe the contents of registers used as temp, thus we need to reset. - Value location = op.temp.curr[i]; - this.values.put(location, UnknownAllocationState.INSTANCE); - } - } - - protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { - if (virtMove.location instanceof RegisterValue) { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); - } else if (LIRValueUtil.isVariable(virtMove.location)) { - // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD - // Move before allocation - // TODO: maybe handle this better than VirtualMove with location as Variable - // TestCase: BoxingTest.boxBoolean - var locations = this.values.getValueLocations(virtMove.variableOrConstant); - for (var location : locations) { - this.values.put(location, new ValueAllocationState(virtMove.location, virtMove)); - } - } else { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); - } - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.LIRKindWithCast; +import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.CastValue; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.Value; +import jdk.vm.ci.meta.ValueKind; + +public class MergedBlockVerifierState { + public MergedAllocationStateMap values; + + protected PhiResolution phiResolution; + protected RegisterAllocationConfig registerAllocationConfig; + + protected ConflictResolver conflictConstantResolver; + protected ConflictResolver labelConflictResolver; + + public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { + this.values = new MergedAllocationStateMap(registerAllocationConfig); + this.phiResolution = phiResolution; + this.registerAllocationConfig = registerAllocationConfig; + this.conflictConstantResolver = constantConflictResolver; + this.labelConflictResolver = labelConflictResolver; + } + + protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { + this.phiResolution = phiResolution; + this.registerAllocationConfig = registerAllocationConfig; + this.conflictConstantResolver = constantConflictResolver; + this.labelConflictResolver = labelConflictResolver; + + if (other == null) { + this.values = new MergedAllocationStateMap(registerAllocationConfig); + return; + } + + this.values = new MergedAllocationStateMap(other.values); + } + + public MergedAllocationStateMap getValues() { + return values; + } + + // TODO: reconsider the merging/meeting logic + // It might make sense to only keep certain states if it is certain to be defined / have a value present + // If a new value was defined in one branch in a certain register and other branches do not have anything in this + // register, then the value could not be defined at merging block and if used later it could be verified as valid + // but it is not. + // If overwritten in one branch but block before branching also has a certain value here + // it would just be marked as unknown / conflicted -> Only care if value after merge is ValueAllocatedState! + public boolean meetWith(MergedBlockVerifierState other) { + return this.values.mergeWith(other.getValues()); + } + + protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block, RAVInstruction.Op labelOp) { + // Check that incoming values are not unknown or conflicted - these only matter if used + for (int idx = 0; idx < values.count; idx++) { + var orig = values.orig[idx]; + var curr = values.curr[idx]; + + assert orig != null; + + boolean isJump = op.lirInstruction instanceof StandardOp.JumpOp; + if (curr == null) { + if (isJump) { + if (phiResolution == PhiResolution.FromUsage || phiResolution == PhiResolution.FromUsageGlobal) { + // Variable has no usage, thus no location present. + continue; + } + + if (phiResolution == PhiResolution.ByAllocator || phiResolution == PhiResolution.FromConflicts) { + continue; + } + + throw new LabelNotResolvedError(block, labelOp, phiResolution); + } + + throw new MissingLocationError(op.lirInstruction, block, orig); + } + + if (orig.equals(curr)) { + // In this case nothing has changed so we have nothing to verify + continue; + } + + if (!kindsEqual(orig, curr) && !isJump) { + throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); + } + + AllocationState state = this.values.get(curr); + if (state.isUnknown()) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + if (state.isConflicted()) { + var variable = LIRValueUtil.asVariable(orig); + var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); + + if (phiResolution == PhiResolution.FromConflicts && resolvedState == null) { + resolvedState = this.labelConflictResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); + if (resolvedState == null) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + } + + this.values.put(curr, resolvedState); + continue; + } + + if (state instanceof ValueAllocationState valAllocState) { + if (!kindsEqualFromState(orig, valAllocState.value)) { + throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); + } + + if (!valAllocState.value.equals(orig)) { + if (orig instanceof CastValue castValue && valAllocState.value.equals(castValue.underlyingValue())) { + continue; // They aren't equal here because of the CastValue, so if they are equal afterwards, we skip next part. + } + + if (valAllocState.value instanceof ConstantValue) { + var variable = LIRValueUtil.asVariable(orig); + var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); + if (resolvedState != null) { + this.values.put(curr, resolvedState); + continue; + } + } + + if (phiResolution == PhiResolution.FromConflicts && LIRValueUtil.isVariable(orig)) { + var variable = LIRValueUtil.asVariable(orig); + var resolvedState = labelConflictResolver.resolveValueState(variable, valAllocState, curr); + if (resolvedState != null) { + this.values.put(curr, resolvedState); + continue; + } + } + + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + continue; + } + + throw GraalError.shouldNotReachHere("Invalid state " + state); + } + } + + protected boolean kindsEqual(Value orig, Value curr) { + var origKind = orig.getValueKind(); + var currKind = curr.getValueKind(); + + if (currKind.equals(origKind)) { + return true; + } + + if (origKind instanceof LIRKindWithCast || currKind instanceof LIRKindWithCast) { + // TestCase: BoxingTest.boxShort + // MOV (x: [v11|QWORD[.] + 12], y: reinterpret: v0|DWORD as: WORD) size: WORD + // MOV (x: [rax|QWORD[.] + 12], y: r10|WORD(DWORD)) size: WORD + return origKind.getPlatformKind().equals(currKind.getPlatformKind()); + } + + // TODO: maybe we should also look at the type before cast + // LIRKindWithCast.getActualKind() + // CastValue.underlyingValue().getValueKind() + return false; + } + + protected boolean kindsEqualFromState(Value orig, Value fromState) { + ValueKind origKind = orig.getValueKind(); + ValueKind currKind = fromState.getValueKind(); + if (orig instanceof CastValue castOrig) { + origKind = castOrig.underlyingValue().getValueKind(); + } + + return origKind.equals(currKind); + } + + protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock block) { + for (int i = 0; i < instruction.alive.count; i++) { + Value value = instruction.alive.curr[i]; + if (Value.ILLEGAL.equals(value)) { + continue; // TODO: remove IllegalValues from these arrays. + } + + for (int j = 0; j < instruction.temp.count; j++) { + if (value.equals(instruction.temp.curr[j])) { + throw new AliveConstraintViolationException(instruction.lirInstruction, block, value, false); + } + } + + for (int j = 0; j < instruction.dests.count; j++) { + if (value.equals(instruction.dests.curr[j])) { + throw new AliveConstraintViolationException(instruction.lirInstruction, block, value, true); + } + } + } + } + + public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstruction.Op labelOp) { + if (instruction instanceof RAVInstruction.Op op) { + checkInputs(op.uses, op, block, labelOp); + checkInputs(op.alive, op, block, labelOp); + + for (int i = 0; i < op.temp.count; i++) { + var curr = op.temp.curr[i]; + var orig = op.temp.orig[i]; + + if (!kindsEqual(orig, curr)) { + // Make sure the assigned register has the correct kind for temp. + throw new KindsMismatchException(instruction.lirInstruction, block, orig, curr, true); + } + } + + this.checkInputs(op.stateValues, op, block, labelOp); + + int kindIdx = 0; + for (int i = 0; i < op.stateValues.count; i++) { + var orig = op.stateValues.orig[i]; + var curr = op.stateValues.curr[i]; + + var state = this.values.get(curr); + if (state instanceof ValueAllocationState valueAllocationState && valueAllocationState.getValue().equals(orig)) { + continue; + } + + JavaKind kind = null; + while (kindIdx < op.kinds.length) { + JavaKind target = op.kinds[kindIdx++]; + if (!JavaKind.Illegal.equals(target)) { + kind = target; + break; + } + // Illegal values are ignored when iterating over state values + // but kept in the kinds array so we need to skip them. + } + + if (!JavaKind.Object.equals(kind)) { + continue; + } + + // TODO: how to handle object kind? + // If the same virtual value is present in the register then there isn't anything to be done + // but maybe if it was changed we also maybe want to make sure that there's a pointer (Object) present? + // but for that we would need to track that information based on GC instructions + } + + this.checkAliveConstraint(op, block); + } + } + + public void update(RAVInstruction.Base instruction, BasicBlock block) { + switch (instruction) { + case RAVInstruction.Op op -> this.updateWithOp(op, block); + case RAVInstruction.VirtualMove virtMove -> this.updateWithVirtualMove(virtMove); + case RAVInstruction.Move move -> this.values.putClone(move.to, this.values.get(move.from)); + default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); + } + } + + protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { + for (int i = 0; i < op.dests.count; i++) { + if (Value.ILLEGAL.equals(op.dests.orig[i])) { + continue; // Safe to ignore, when destination is illegal value, not when used. + } + + assert op.dests.orig[i] != null; + + if (op.dests.curr[i] == null) { + if (phiResolution == PhiResolution.FromJump) { + throw new LabelNotResolvedError(block, op, phiResolution); + } + + continue; + } + + Value location = op.dests.curr[i]; + Value variable = op.dests.orig[i]; + + if (location.equals(variable)) { + // Only check register validity if it was changed by the register allocator + // for example: rbp is used as input to start block and forbidden to be used by the allocator + this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op)); + } else { + this.values.put(location, new ValueAllocationState(variable, op)); + } + } + + for (int i = 0; i < op.temp.count; i++) { + var value = op.temp.curr[i]; + if (Value.ILLEGAL.equals(value)) { + continue; + } + + // We cannot believe the contents of registers used as temp, thus we need to reset. + Value location = op.temp.curr[i]; + this.values.put(location, UnknownAllocationState.INSTANCE); + } + } + + protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { + if (virtMove.location instanceof RegisterValue) { + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); + } else if (LIRValueUtil.isVariable(virtMove.location)) { + // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD + // Move before allocation + // TODO: maybe handle this better than VirtualMove with location as Variable + // TestCase: BoxingTest.boxBoolean + var locations = this.values.getValueLocations(virtMove.variableOrConstant); + for (var location : locations) { + this.values.put(location, new ValueAllocationState(virtMove.location, virtMove)); + } + } else { + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java index cfb7063bad82..4e3774c44423 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java @@ -1,16 +1,16 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.vm.ci.meta.Value; - -@SuppressWarnings("serial") -public class MissingLocationError extends RAVError { - public MissingLocationError(LIRInstruction instruction, BasicBlock block, Value variable) { - super(MissingLocationError.getMessage(instruction, block, variable)); - } - - static String getMessage(LIRInstruction instruction, BasicBlock block, Value variable) { - return "Variable " + variable + " is missing a location in " + instruction + " in block " + block; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +@SuppressWarnings("serial") +public class MissingLocationError extends RAVError { + public MissingLocationError(LIRInstruction instruction, BasicBlock block, Value variable) { + super(MissingLocationError.getMessage(instruction, block, variable)); + } + + static String getMessage(LIRInstruction instruction, BasicBlock block, Value variable) { + return "Variable " + variable + " is missing a location in " + instruction + " in block " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java index ef162a0af02f..a390e68798ef 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java @@ -1,10 +1,10 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -public enum PhiResolution { - FromJump, - FromUsage, - FromPredecessors, - FromUsageGlobal, - FromConflicts, - ByAllocator -} +package jdk.graal.compiler.lir.alloc.verifier; + +public enum PhiResolution { + FromJump, + FromUsage, + FromPredecessors, + FromUsageGlobal, + FromConflicts, + ByAllocator +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index bcf5bb1176ba..74cfda05f681 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -1,230 +1,230 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.InstructionStateProcedure; -import jdk.graal.compiler.lir.InstructionValueProcedure; -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.StandardOp; -import jdk.graal.compiler.lir.ValueProcedure; -import jdk.graal.compiler.lir.Variable; -import jdk.graal.compiler.lir.gen.LIRGenerationResult; -import jdk.graal.compiler.lir.gen.LIRGenerator; -import jdk.graal.compiler.lir.phases.AllocationPhase; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.meta.Value; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class PreRegisterAllocationPhase extends AllocationPhase { - protected TaggedConstantFactory taggedConstantFactory; - protected RegisterAllocationVerifierPhaseState state; - - public PreRegisterAllocationPhase(RegisterAllocationVerifierPhaseState state) { - this.taggedConstantFactory = new TaggedConstantFactory(); - this.state = state; - } - - public static class ConstantOverrideValueProcedure implements ValueProcedure { - private LIR lir; - private List variables; - private Map constantValueMap; - private Method nextVariableMethod; - - public ConstantOverrideValueProcedure(LIR lir, Map constantValueMap) { - this.lir = lir; - this.constantValueMap = constantValueMap; - - try { - this.nextVariableMethod = LIRGenerator.VariableProvider.class.getDeclaredMethod("nextVariable"); - this.nextVariableMethod.setAccessible(true); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - public void setVariableList(List variables) { - this.variables = variables; - } - - public int nextVariable() { - try { - return (int) nextVariableMethod.invoke((LIRGenerator.VariableProvider) this.lir); - } catch (InvocationTargetException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (value instanceof ConstantValue constant) { - var variable = new Variable(constant.getValueKind(), nextVariable()); - constantValueMap.put(variable, constant); - variables.add(variable); - return variable; - } - - return value; - } - } - - protected InstructionValueProcedure taggedConstantValueProc = new InstructionValueProcedure() { - @Override - public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (value instanceof ConstantValue constantValue) { - return new ConstantValue(constantValue.getValueKind(), taggedConstantFactory.createNew(constantValue.getConstant())); - } - - return value; - } - }; - - @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - if (!state.shouldBeVerified(lirGenRes)) { - return; - } - - var preallocMap = state.createInstructionMap(lirGenRes); - LIR lir = lirGenRes.getLIR(); - Map constantValueMap = new HashMap<>(); - var constOverwriteProc = new ConstantOverrideValueProcedure(lir, constantValueMap); - - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - ArrayList instructions = lir.getLIRforBlock(block); - - List newVars = new ArrayList<>(); - constOverwriteProc.setVariableList(newVars); - - RAVInstruction.Base previousInstr = null; - for (var instruction : instructions) { - if (instruction instanceof StandardOp.JumpOp && this.state.phiResolution == PhiResolution.FromJump) { - if (this.state.moveConstants) { - instruction.forEachAlive(constOverwriteProc); - } else { - instruction.forEachAlive(taggedConstantValueProc); - } - } - - if (this.isVirtualMove(instruction)) { - // Virtual moves (variable = MOV real register) are going to be removed by the allocator, - // but we still need the information about which variables are associated to which real - // registers, and so we store them. They are generally associated to other instructions - // that's why we append them here to the previous instruction (for example Label or Foreign Call) - // use these, if this instruction was deleted in the allocator, then they will be missing too. - assert previousInstr != null; - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var location = valueMov.getInput(); - var variable = LIRValueUtil.asVariable(valueMov.getResult()); - - var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, location); - previousInstr.addVirtualMove(virtualMove); - continue; // No need to store virtual move here, it is stored into previous instruction. - } - - boolean speculative = false; - if (this.isSpeculativeMove(instruction)) { - speculative = true; - // Speculative moves are in form ry = MOVE vx, which could be removed if variable - // ends up being allocated to the same register as ry. If it was removed - // we need to re-add it because it holds important information about where value of - // this variable is placed - for label resolution after the label. - assert previousInstr != null; - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var variable = LIRValueUtil.asVariable(valueMov.getInput()); - var register = valueMov.getResult(); - - var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); - previousInstr.addSpeculativeMove(virtualMove); - } - - var opRAVInstr = new RAVInstruction.Op(instruction); - - instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); - instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); - instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); - instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); - instruction.forEachState(opRAVInstr.stateValues.copyOriginalProc); - instruction.forEachState(new InstructionStateProcedure() { - @Override - public void doState(LIRInstruction instruction, LIRFrameState state) { - if (state.topFrame == null) { - return; - } - - // Haven't found a case where there is multiple frame states on an instruction - // so this will work, otherwise appending them would do the job in that case - // if we could also get this information about VirtualObjects. - opRAVInstr.kinds = state.topFrame.getSlotKinds(); - } - }); - - preallocMap.put(instruction, opRAVInstr); - - if (!speculative) { - previousInstr = opRAVInstr; - } - } - - if (newVars.isEmpty()) { - continue; - } - - var j = instructions.removeLast(); - var it = newVars.iterator(); - while (it.hasNext()) { - var v = LIRValueUtil.asVariable(it.next()); - var mov = context.spillMoveFactory.createMove(v, constantValueMap.get(v)); - var ravInstr = new RAVInstruction.Op(mov); - mov.forEachOutput(ravInstr.dests.copyOriginalProc); - preallocMap.put(mov, ravInstr); - instructions.add(mov); - } - - instructions.add(j); - } - } - - /** - * Determines if instruction is a virtual move, a virtual move is - * a move instruction that moves a real register value into a variable, - * which is something that will always get removed from the final allocated - * IR. - * - * @param instruction LIRInstruction we are looking at - * @return true, if instruction is a virtual move, otherwise false - */ - protected boolean isVirtualMove(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - return false; - } - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var input = valueMov.getInput(); - return (input instanceof RegisterValue || input instanceof StackSlot /*|| input instanceof AbstractAddress*/) && LIRValueUtil.isVariable(valueMov.getResult()); - } - - protected boolean isSpeculativeMove(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - return false; - } - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var result = valueMov.getResult(); // Result could be variable or register - return (result instanceof RegisterValue || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.InstructionStateProcedure; +import jdk.graal.compiler.lir.InstructionValueProcedure; +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.StandardOp; +import jdk.graal.compiler.lir.ValueProcedure; +import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.gen.LIRGenerationResult; +import jdk.graal.compiler.lir.gen.LIRGenerator; +import jdk.graal.compiler.lir.phases.AllocationPhase; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.Value; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PreRegisterAllocationPhase extends AllocationPhase { + protected TaggedConstantFactory taggedConstantFactory; + protected RegisterAllocationVerifierPhaseState state; + + public PreRegisterAllocationPhase(RegisterAllocationVerifierPhaseState state) { + this.taggedConstantFactory = new TaggedConstantFactory(); + this.state = state; + } + + public static class ConstantOverrideValueProcedure implements ValueProcedure { + private LIR lir; + private List variables; + private Map constantValueMap; + private Method nextVariableMethod; + + public ConstantOverrideValueProcedure(LIR lir, Map constantValueMap) { + this.lir = lir; + this.constantValueMap = constantValueMap; + + try { + this.nextVariableMethod = LIRGenerator.VariableProvider.class.getDeclaredMethod("nextVariable"); + this.nextVariableMethod.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public void setVariableList(List variables) { + this.variables = variables; + } + + public int nextVariable() { + try { + return (int) nextVariableMethod.invoke((LIRGenerator.VariableProvider) this.lir); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (value instanceof ConstantValue constant) { + var variable = new Variable(constant.getValueKind(), nextVariable()); + constantValueMap.put(variable, constant); + variables.add(variable); + return variable; + } + + return value; + } + } + + protected InstructionValueProcedure taggedConstantValueProc = new InstructionValueProcedure() { + @Override + public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (value instanceof ConstantValue constantValue) { + return new ConstantValue(constantValue.getValueKind(), taggedConstantFactory.createNew(constantValue.getConstant())); + } + + return value; + } + }; + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + if (!state.shouldBeVerified(lirGenRes)) { + return; + } + + var preallocMap = state.createInstructionMap(lirGenRes); + LIR lir = lirGenRes.getLIR(); + Map constantValueMap = new HashMap<>(); + var constOverwriteProc = new ConstantOverrideValueProcedure(lir, constantValueMap); + + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + ArrayList instructions = lir.getLIRforBlock(block); + + List newVars = new ArrayList<>(); + constOverwriteProc.setVariableList(newVars); + + RAVInstruction.Base previousInstr = null; + for (var instruction : instructions) { + if (instruction instanceof StandardOp.JumpOp && this.state.phiResolution == PhiResolution.FromJump) { + if (this.state.moveConstants) { + instruction.forEachAlive(constOverwriteProc); + } else { + instruction.forEachAlive(taggedConstantValueProc); + } + } + + if (this.isVirtualMove(instruction)) { + // Virtual moves (variable = MOV real register) are going to be removed by the allocator, + // but we still need the information about which variables are associated to which real + // registers, and so we store them. They are generally associated to other instructions + // that's why we append them here to the previous instruction (for example Label or Foreign Call) + // use these, if this instruction was deleted in the allocator, then they will be missing too. + assert previousInstr != null; + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var location = valueMov.getInput(); + var variable = LIRValueUtil.asVariable(valueMov.getResult()); + + var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, location); + previousInstr.addVirtualMove(virtualMove); + continue; // No need to store virtual move here, it is stored into previous instruction. + } + + boolean speculative = false; + if (this.isSpeculativeMove(instruction)) { + speculative = true; + // Speculative moves are in form ry = MOVE vx, which could be removed if variable + // ends up being allocated to the same register as ry. If it was removed + // we need to re-add it because it holds important information about where value of + // this variable is placed - for label resolution after the label. + assert previousInstr != null; + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var variable = LIRValueUtil.asVariable(valueMov.getInput()); + var register = valueMov.getResult(); + + var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); + previousInstr.addSpeculativeMove(virtualMove); + } + + var opRAVInstr = new RAVInstruction.Op(instruction); + + instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); + instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); + instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); + instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); + instruction.forEachState(opRAVInstr.stateValues.copyOriginalProc); + instruction.forEachState(new InstructionStateProcedure() { + @Override + public void doState(LIRInstruction instruction, LIRFrameState state) { + if (state.topFrame == null) { + return; + } + + // Haven't found a case where there is multiple frame states on an instruction + // so this will work, otherwise appending them would do the job in that case + // if we could also get this information about VirtualObjects. + opRAVInstr.kinds = state.topFrame.getSlotKinds(); + } + }); + + preallocMap.put(instruction, opRAVInstr); + + if (!speculative) { + previousInstr = opRAVInstr; + } + } + + if (newVars.isEmpty()) { + continue; + } + + var j = instructions.removeLast(); + var it = newVars.iterator(); + while (it.hasNext()) { + var v = LIRValueUtil.asVariable(it.next()); + var mov = context.spillMoveFactory.createMove(v, constantValueMap.get(v)); + var ravInstr = new RAVInstruction.Op(mov); + mov.forEachOutput(ravInstr.dests.copyOriginalProc); + preallocMap.put(mov, ravInstr); + instructions.add(mov); + } + + instructions.add(j); + } + } + + /** + * Determines if instruction is a virtual move, a virtual move is + * a move instruction that moves a real register value into a variable, + * which is something that will always get removed from the final allocated + * IR. + * + * @param instruction LIRInstruction we are looking at + * @return true, if instruction is a virtual move, otherwise false + */ + protected boolean isVirtualMove(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return false; + } + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var input = valueMov.getInput(); + return (input instanceof RegisterValue || input instanceof StackSlot /*|| input instanceof AbstractAddress*/) && LIRValueUtil.isVariable(valueMov.getResult()); + } + + protected boolean isSpeculativeMove(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return false; + } + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var result = valueMov.getResult(); // Result could be variable or register + return (result instanceof RegisterValue || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java index 34e15989b125..1763929246bb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java @@ -1,9 +1,9 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -@SuppressWarnings("serial") -public class RAVError extends Error { - // These shouldn't happen within the verifier - public RAVError(String message) { - super(message); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +@SuppressWarnings("serial") +public class RAVError extends Error { + // These shouldn't happen within the verifier + public RAVError(String message) { + super(message); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java index 54b57809ca76..1a7e3f3e3feb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java @@ -1,8 +1,8 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -@SuppressWarnings("serial") -public class RAVException extends RuntimeException { - public RAVException(String message) { - super(message); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +@SuppressWarnings("serial") +public class RAVException extends RuntimeException { + public RAVException(String message) { + super(message); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java index bf1c7147ac83..a58239a07f8c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -1,22 +1,22 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import java.util.List; - -@SuppressWarnings("serial") -public class RAVFailedVerificationException extends RAVException { - public RAVFailedVerificationException(String compUnitName, List exceptions) { - super(RAVFailedVerificationException.getMessage(compUnitName, exceptions)); - } - - static String getMessage(String compUnitName, List exceptions) { - StringBuilder sb = new StringBuilder("Failed to verify "); - sb.append(compUnitName); - sb.append(":"); - for (var e : exceptions) { - sb.append(" - "); - sb.append(e.getMessage()); - sb.append("\n"); - } - return sb.toString(); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import java.util.List; + +@SuppressWarnings("serial") +public class RAVFailedVerificationException extends RAVException { + public RAVFailedVerificationException(String compUnitName, List exceptions) { + super(RAVFailedVerificationException.getMessage(compUnitName, exceptions)); + } + + static String getMessage(String compUnitName, List exceptions) { + StringBuilder sb = new StringBuilder("Failed to verify "); + sb.append(compUnitName); + sb.append(":"); + for (var e : exceptions) { + sb.append(" - "); + sb.append(e.getMessage()); + sb.append("\n"); + } + return sb.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 7f5aab9384b6..aaf88cc9d1cb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -3,10 +3,7 @@ import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.vm.ci.meta.Value; import java.util.ArrayList; import java.util.LinkedList; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 9e58b8e59ab1..731f5e02c553 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -3,8 +3,6 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.debug.DebugOptions; -import jdk.graal.compiler.debug.MethodFilter; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java index 6ad5d7820862..4431d658f431 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -1,49 +1,49 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.graal.compiler.lir.gen.LIRGenerationResult; -import jdk.graal.compiler.options.OptionValues; - -import java.util.IdentityHashMap; -import java.util.Map; - -public class RegisterAllocationVerifierPhaseState { - public PhiResolution phiResolution; - public boolean moveConstants; - public String filterStr; - - protected Map> verifierInstructions; - - public RegisterAllocationVerifierPhaseState(OptionValues options) { - this.verifierInstructions = new IdentityHashMap<>(); - - this.phiResolution = RegisterAllocationVerifierPhase.Options.RAPhiResolution.getValue(options); - this.moveConstants = RegisterAllocationVerifierPhase.Options.MoveConstants.getValue(options); - this.filterStr = RegisterAllocationVerifierPhase.Options.RAFilter.getValue(options); - } - - public boolean shouldBeVerified(LIRGenerationResult lirGenRes) { - var compUnitName = lirGenRes.getCompilationUnitName(); - // Filter for compilation unit substring to run verification only on - // certain methods, cannot use MethodFilter here because I cannot - // access JavaMethod here. - return filterStr == null || compUnitName.contains(filterStr); - } - - public Map createInstructionMap(LIRGenerationResult lirGenRes) { - this.verifierInstructions.put(lirGenRes, new IdentityHashMap<>()); - return this.verifierInstructions.get(lirGenRes); - } - - public Map getInstructionMap(LIRGenerationResult lirGenRes) { - if (!this.verifierInstructions.containsKey(lirGenRes)) { - throw new IllegalStateException(); - } - - return this.verifierInstructions.get(lirGenRes); - } - - public void deleteInstructionMap(LIRGenerationResult lirGenRes) { - verifierInstructions.remove(lirGenRes); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.gen.LIRGenerationResult; +import jdk.graal.compiler.options.OptionValues; + +import java.util.IdentityHashMap; +import java.util.Map; + +public class RegisterAllocationVerifierPhaseState { + public PhiResolution phiResolution; + public boolean moveConstants; + public String filterStr; + + protected Map> verifierInstructions; + + public RegisterAllocationVerifierPhaseState(OptionValues options) { + this.verifierInstructions = new IdentityHashMap<>(); + + this.phiResolution = RegisterAllocationVerifierPhase.Options.RAPhiResolution.getValue(options); + this.moveConstants = RegisterAllocationVerifierPhase.Options.MoveConstants.getValue(options); + this.filterStr = RegisterAllocationVerifierPhase.Options.RAFilter.getValue(options); + } + + public boolean shouldBeVerified(LIRGenerationResult lirGenRes) { + var compUnitName = lirGenRes.getCompilationUnitName(); + // Filter for compilation unit substring to run verification only on + // certain methods, cannot use MethodFilter here because I cannot + // access JavaMethod here. + return filterStr == null || compUnitName.contains(filterStr); + } + + public Map createInstructionMap(LIRGenerationResult lirGenRes) { + this.verifierInstructions.put(lirGenRes, new IdentityHashMap<>()); + return this.verifierInstructions.get(lirGenRes); + } + + public Map getInstructionMap(LIRGenerationResult lirGenRes) { + if (!this.verifierInstructions.containsKey(lirGenRes)) { + throw new IllegalStateException(); + } + + return this.verifierInstructions.get(lirGenRes); + } + + public void deleteInstructionMap(LIRGenerationResult lirGenRes) { + verifierInstructions.remove(lirGenRes); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java index d75ef6828ce4..e10c800d687c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java @@ -1,33 +1,33 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.type.DataPointerConstant; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.JavaConstant; - -public class TaggedConstantFactory { - protected int lastTag; - - public TaggedConstantFactory(int initialTag) { - this.lastTag = initialTag; - } - - public TaggedConstantFactory() { - this(0); - } - - public Constant createNew(Constant constant) { - return switch (constant) { - case JavaConstant javaConst -> this.createNew(javaConst); - case DataPointerConstant ptrConst -> this.createNew(ptrConst); - default -> throw new IllegalStateException(); - }; - } - - public Constant createNew(JavaConstant constant) { - return new TaggedJavaConstant(constant, this.lastTag++); - } - - public Constant createNew(DataPointerConstant constant) { - return new TaggedDataPointerConstant(constant, this.lastTag++); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.type.DataPointerConstant; +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaConstant; + +public class TaggedConstantFactory { + protected int lastTag; + + public TaggedConstantFactory(int initialTag) { + this.lastTag = initialTag; + } + + public TaggedConstantFactory() { + this(0); + } + + public Constant createNew(Constant constant) { + return switch (constant) { + case JavaConstant javaConst -> this.createNew(javaConst); + case DataPointerConstant ptrConst -> this.createNew(ptrConst); + default -> throw new IllegalStateException(); + }; + } + + public Constant createNew(JavaConstant constant) { + return new TaggedJavaConstant(constant, this.lastTag++); + } + + public Constant createNew(DataPointerConstant constant) { + return new TaggedDataPointerConstant(constant, this.lastTag++); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java index 329d83380831..d233a78dbf51 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java @@ -1,54 +1,54 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.type.DataPointerConstant; - -import java.nio.ByteBuffer; - -public class TaggedDataPointerConstant extends DataPointerConstant { - protected DataPointerConstant constant; - protected int tag; - - protected TaggedDataPointerConstant(DataPointerConstant constant, int tag) { - super(constant.getAlignment()); - this.constant = constant; - this.tag = tag; - } - - @Override - public int getSerializedSize() { - return this.constant.getSerializedSize(); - } - - @Override - public void serialize(ByteBuffer buffer) { - this.constant.serialize(buffer); - } - - @Override - public String toValueString() { - return this.constant.toValueString(); - } - - @Override - public int hashCode() { - return this.tag ^ constant.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o instanceof TaggedDataPointerConstant otherTagged) { - return otherTagged.tag == tag && this.constant.equals(otherTagged.constant); - } - - return constant.equals(o); - } - - @Override - public String toString() { - return "t" + this.tag + ":" + constant.toString(); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.type.DataPointerConstant; + +import java.nio.ByteBuffer; + +public class TaggedDataPointerConstant extends DataPointerConstant { + protected DataPointerConstant constant; + protected int tag; + + protected TaggedDataPointerConstant(DataPointerConstant constant, int tag) { + super(constant.getAlignment()); + this.constant = constant; + this.tag = tag; + } + + @Override + public int getSerializedSize() { + return this.constant.getSerializedSize(); + } + + @Override + public void serialize(ByteBuffer buffer) { + this.constant.serialize(buffer); + } + + @Override + public String toValueString() { + return this.constant.toValueString(); + } + + @Override + public int hashCode() { + return this.tag ^ constant.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof TaggedDataPointerConstant otherTagged) { + return otherTagged.tag == tag && this.constant.equals(otherTagged.constant); + } + + return constant.equals(o); + } + + @Override + public String toString() { + return "t" + this.tag + ":" + constant.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java index 0c7fad8fb336..9d20be648f7d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java @@ -1,87 +1,87 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; - -public class TaggedJavaConstant implements JavaConstant { - protected JavaConstant constant; - protected int tag; - - protected TaggedJavaConstant(JavaConstant constant, int tag) { - this.constant = constant; - this.tag = tag; - } - - @Override - public int hashCode() { - return this.tag ^ constant.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o instanceof TaggedJavaConstant otherTagged) { - return otherTagged.tag == tag && this.constant.equals(otherTagged.constant); - } - - return constant.equals(o); - } - - @Override - public String toString() { - return "t" + this.tag + ":" + constant.toString(); - } - - @Override - public JavaKind getJavaKind() { - return this.constant.getJavaKind(); - } - - @Override - public boolean isNull() { - return this.constant.isNull(); - } - - @Override - public boolean isDefaultForKind() { - return this.constant.isDefaultForKind(); - } - - @Override - public Object asBoxedPrimitive() { - return this.constant.asBoxedPrimitive(); - } - - @Override - public int asInt() { - return this.constant.asInt(); - } - - @Override - public boolean asBoolean() { - return this.constant.asBoolean(); - } - - @Override - public long asLong() { - return this.constant.asLong(); - } - - @Override - public float asFloat() { - return this.constant.asFloat(); - } - - @Override - public double asDouble() { - return this.constant.asDouble(); - } - - @Override - public String toValueString() { - return this.constant.toValueString(); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; + +public class TaggedJavaConstant implements JavaConstant { + protected JavaConstant constant; + protected int tag; + + protected TaggedJavaConstant(JavaConstant constant, int tag) { + this.constant = constant; + this.tag = tag; + } + + @Override + public int hashCode() { + return this.tag ^ constant.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof TaggedJavaConstant otherTagged) { + return otherTagged.tag == tag && this.constant.equals(otherTagged.constant); + } + + return constant.equals(o); + } + + @Override + public String toString() { + return "t" + this.tag + ":" + constant.toString(); + } + + @Override + public JavaKind getJavaKind() { + return this.constant.getJavaKind(); + } + + @Override + public boolean isNull() { + return this.constant.isNull(); + } + + @Override + public boolean isDefaultForKind() { + return this.constant.isDefaultForKind(); + } + + @Override + public Object asBoxedPrimitive() { + return this.constant.asBoxedPrimitive(); + } + + @Override + public int asInt() { + return this.constant.asInt(); + } + + @Override + public boolean asBoolean() { + return this.constant.asBoolean(); + } + + @Override + public long asLong() { + return this.constant.asLong(); + } + + @Override + public float asFloat() { + return this.constant.asFloat(); + } + + @Override + public double asDouble() { + return this.constant.asDouble(); + } + + @Override + public String toValueString() { + return this.constant.toValueString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java index 86511548c979..0966b7623ce4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java @@ -1,19 +1,19 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; - -@SuppressWarnings("serial") -public class TargetLocationOverwrittenException extends RAVException { - public RAVInstruction.VirtualMove virtualMove; - public BasicBlock block; - - public TargetLocationOverwrittenException(RAVInstruction.VirtualMove move, BasicBlock block) { - super(getErrorMessage(move, block)); - this.virtualMove = move; - this.block = block; - } - - static String getErrorMessage(RAVInstruction.VirtualMove move, BasicBlock block) { - return "Target location " + move.location + " was overwritten by " + move.lirInstruction + " in " + block; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; + +@SuppressWarnings("serial") +public class TargetLocationOverwrittenException extends RAVException { + public RAVInstruction.VirtualMove virtualMove; + public BasicBlock block; + + public TargetLocationOverwrittenException(RAVInstruction.VirtualMove move, BasicBlock block) { + super(getErrorMessage(move, block)); + this.virtualMove = move; + this.block = block; + } + + static String getErrorMessage(RAVInstruction.VirtualMove move, BasicBlock block) { + return "Target location " + move.location + " was overwritten by " + move.lirInstruction + " in " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index 590ad9900793..f7bcea2e677f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -1,30 +1,30 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -public class UnknownAllocationState extends AllocationState { - public static UnknownAllocationState INSTANCE = new UnknownAllocationState(); - - @Override - public boolean isUnknown() { - return true; - } - - @Override - public AllocationState meet(AllocationState other) { - return other; - } - - @Override - public AllocationState clone() { - return INSTANCE; - } - - @Override - public boolean equals(AllocationState other) { - return other == INSTANCE; - } - - @Override - public String toString() { - return "Unknown"; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +public class UnknownAllocationState extends AllocationState { + public static UnknownAllocationState INSTANCE = new UnknownAllocationState(); + + @Override + public boolean isUnknown() { + return true; + } + + @Override + public AllocationState meet(AllocationState other) { + return other; + } + + @Override + public AllocationState clone() { + return INSTANCE; + } + + @Override + public boolean equals(AllocationState other) { + return other == INSTANCE; + } + + @Override + public String toString() { + return "Unknown"; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java index 006c73d0722c..243a0a2e95d4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java @@ -1,30 +1,30 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.graal.compiler.lir.StandardOp; - -@SuppressWarnings("serial") -public class UnknownInstructionError extends RAVError { - public LIRInstruction instruction; - public BasicBlock block; - - public UnknownInstructionError(LIRInstruction instruction, BasicBlock block) { - super(UnknownInstructionError.getErrorMessage(instruction, block)); - - this.instruction = instruction; - this.block = block; - } - - static String getErrorMessage(LIRInstruction instruction, BasicBlock block) { - if (instruction.isMoveOp()) { - return "Unknown MOVE " + instruction + " in " + block; - } - - if (instruction instanceof StandardOp.LabelOp) { - return "Unknown LABEL " + instruction + " in " + block; - } - - return "Unknown instruction for RAV " + instruction + " in " + block; - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.StandardOp; + +@SuppressWarnings("serial") +public class UnknownInstructionError extends RAVError { + public LIRInstruction instruction; + public BasicBlock block; + + public UnknownInstructionError(LIRInstruction instruction, BasicBlock block) { + super(UnknownInstructionError.getErrorMessage(instruction, block)); + + this.instruction = instruction; + this.block = block; + } + + static String getErrorMessage(LIRInstruction instruction, BasicBlock block) { + if (instruction.isMoveOp()) { + return "Unknown MOVE " + instruction + " in " + block; + } + + if (instruction instanceof StandardOp.LabelOp) { + return "Unknown LABEL " + instruction + " in " + block; + } + + return "Unknown instruction for RAV " + instruction + " in " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index ab9375d4d6e9..6f9384d97f97 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -1,78 +1,78 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.VirtualStackSlot; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.meta.Value; - -public class ValueAllocationState extends AllocationState implements Cloneable { - protected Value value; - protected RAVInstruction.Base source; - - public ValueAllocationState(Value value, RAVInstruction.Base source) { - if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { - // StackSlot, RegisterValue is present in start block in label as predefined argument - // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS - // ConstantValue act as Variable - - // We use variables as symbols for register validation - // but real registers can also be used as that, in some cases. - this.value = value; - this.source = source; - } else { - throw GraalError.shouldNotReachHere("Invalid type of value used " + value); - } - } - - public ValueAllocationState(ValueAllocationState other) { - this.value = other.getValue(); - } - - public Value getValue() { - return value; - } - - public RAVInstruction.Base getSource() { - return source; - } - - public AllocationState meet(AllocationState other) { - if (other.isUnknown()) { - return this; - } - - if (other.isConflicted()) { - return other; - } - - var otherValueAllocState = (ValueAllocationState) other; - if (!this.value.equals(otherValueAllocState.getValue())) { - return new ConflictedAllocationState(this, otherValueAllocState); - } - - return this; - } - - @Override - public boolean equals(AllocationState other) { - return other instanceof ValueAllocationState otherVal && this.value.equals(otherVal.getValue()); - } - - @Override - public ValueAllocationState clone() { - return new ValueAllocationState(this); - } - - @Override - public String toString() { - return "Value {" + this.value + "}"; - } - - @Override - public int hashCode() { - return this.value.hashCode(); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.meta.Value; + +public class ValueAllocationState extends AllocationState implements Cloneable { + protected Value value; + protected RAVInstruction.Base source; + + public ValueAllocationState(Value value, RAVInstruction.Base source) { + if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { + // StackSlot, RegisterValue is present in start block in label as predefined argument + // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS + // ConstantValue act as Variable + + // We use variables as symbols for register validation + // but real registers can also be used as that, in some cases. + this.value = value; + this.source = source; + } else { + throw GraalError.shouldNotReachHere("Invalid type of value used " + value); + } + } + + public ValueAllocationState(ValueAllocationState other) { + this.value = other.getValue(); + } + + public Value getValue() { + return value; + } + + public RAVInstruction.Base getSource() { + return source; + } + + public AllocationState meet(AllocationState other) { + if (other.isUnknown()) { + return this; + } + + if (other.isConflicted()) { + return other; + } + + var otherValueAllocState = (ValueAllocationState) other; + if (!this.value.equals(otherValueAllocState.getValue())) { + return new ConflictedAllocationState(this, otherValueAllocState); + } + + return this; + } + + @Override + public boolean equals(AllocationState other) { + return other instanceof ValueAllocationState otherVal && this.value.equals(otherVal.getValue()); + } + + @Override + public ValueAllocationState clone() { + return new ValueAllocationState(this); + } + + @Override + public String toString() { + return "Value {" + this.value + "}"; + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index b76cc0888c91..3d0628e2c865 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -1,51 +1,51 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.vm.ci.meta.Value; - -@SuppressWarnings("serial") -public class ValueNotInRegisterException extends RAVException { - public LIRInstruction instruction; - public BasicBlock block; - public Value variable; // Can be a constant or other symbolic value - public Value location; // Can be StackSlot, RegisterValue or memory - public AllocationState state; - - public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { - super(ValueNotInRegisterException.getErrorMessage(instruction, block, variable, location, state)); - - this.instruction = instruction; - this.block = block; - this.variable = variable; - this.location = location; - this.state = state; - } - - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { - var messageBuilder = new StringBuilder(); - messageBuilder - .append("Value ") - .append(variable) - .append(" not found in ") - .append(location) - .append(" for instruction ") - .append(instruction) - .append(" in ") - .append(block) - .append(" actual state is "); - - if (state instanceof ConflictedAllocationState confState) { - var confStates = confState.getConflictedStates(); - - messageBuilder.append("\n"); - for (var conflictedState : confStates) { - messageBuilder.append(" - ").append(conflictedState.getValue()).append(" from ").append(conflictedState.source).append("\n"); - } - } else { - messageBuilder.append(state); - } - - return messageBuilder.toString(); - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +@SuppressWarnings("serial") +public class ValueNotInRegisterException extends RAVException { + public LIRInstruction instruction; + public BasicBlock block; + public Value variable; // Can be a constant or other symbolic value + public Value location; // Can be StackSlot, RegisterValue or memory + public AllocationState state; + + public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { + super(ValueNotInRegisterException.getErrorMessage(instruction, block, variable, location, state)); + + this.instruction = instruction; + this.block = block; + this.variable = variable; + this.location = location; + this.state = state; + } + + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { + var messageBuilder = new StringBuilder(); + messageBuilder + .append("Value ") + .append(variable) + .append(" not found in ") + .append(location) + .append(" for instruction ") + .append(instruction) + .append(" in ") + .append(block) + .append(" actual state is "); + + if (state instanceof ConflictedAllocationState confState) { + var confStates = confState.getConflictedStates(); + + messageBuilder.append("\n"); + for (var conflictedState : confStates) { + messageBuilder.append(" - ").append(conflictedState.getValue()).append(" from ").append(conflictedState.source).append("\n"); + } + } else { + messageBuilder.append(state); + } + + return messageBuilder.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index 4604e61a0cb0..51355b0a9d60 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -1,47 +1,46 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; - -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.List; - -public class VerifierPrinter { - public static void print(PrintStream out, LIR lir, BlockMap> instructions) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - - var blockHeaderSB = new StringBuilder(); - blockHeaderSB.append(block.toString()).append(": "); - - if (block.getPredecessorCount() > 0) { - blockHeaderSB.append(" <- "); - for (int i = 0; i < block.getPredecessorCount(); i++) { - blockHeaderSB.append(block.getPredecessorAt(i)).append(", "); - } - - if (!blockHeaderSB.isEmpty()) { - blockHeaderSB.setLength(blockHeaderSB.length() - 2); - } - } - - if (block.getSuccessorCount() > 0) { - blockHeaderSB.append(" -> "); - for (int i = 0; i < block.getSuccessorCount(); i++) { - blockHeaderSB.append(block.getSuccessorAt(i)).append(", "); - } - - if (block.getSuccessorCount() > 0 && !blockHeaderSB.isEmpty()) { - blockHeaderSB.setLength(blockHeaderSB.length() - 2); - } - } - - out.println(blockHeaderSB); - for (var instruction : instructions.get(block)) { - out.println("\t" + instruction.toString() + " | " + instruction.getLIRInstruction().toString()); - } - out.println(); - } - } -} +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; + +import java.io.PrintStream; +import java.util.List; + +public class VerifierPrinter { + public static void print(PrintStream out, LIR lir, BlockMap> instructions) { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + + var blockHeaderSB = new StringBuilder(); + blockHeaderSB.append(block.toString()).append(": "); + + if (block.getPredecessorCount() > 0) { + blockHeaderSB.append(" <- "); + for (int i = 0; i < block.getPredecessorCount(); i++) { + blockHeaderSB.append(block.getPredecessorAt(i)).append(", "); + } + + if (!blockHeaderSB.isEmpty()) { + blockHeaderSB.setLength(blockHeaderSB.length() - 2); + } + } + + if (block.getSuccessorCount() > 0) { + blockHeaderSB.append(" -> "); + for (int i = 0; i < block.getSuccessorCount(); i++) { + blockHeaderSB.append(block.getSuccessorAt(i)).append(", "); + } + + if (block.getSuccessorCount() > 0 && !blockHeaderSB.isEmpty()) { + blockHeaderSB.setLength(blockHeaderSB.length() - 2); + } + } + + out.println(blockHeaderSB); + for (var instruction : instructions.get(block)) { + out.println("\t" + instruction.toString() + " | " + instruction.getLIRInstruction().toString()); + } + out.println(); + } + } +} From ba197a52cfb66ad01a8c823b520832efe0498a71 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 14 Feb 2026 19:36:28 +0100 Subject: [PATCH 044/112] Fix checkstyle issues --- .../verifier/ConflictedAllocationState.java | 7 ++-- ...nstantMaterializationConflictResolver.java | 8 ++--- .../lir/alloc/verifier/DefinitionSet.java | 12 +++---- .../verifier/FromPredecessorsResolver.java | 26 +++++++------- .../lir/alloc/verifier/FromUsageResolver.java | 34 +++++++++++-------- .../verifier/FromUsageResolverGlobal.java | 31 +++++++++-------- .../alloc/verifier/LabelConflictResolver.java | 14 ++++---- .../verifier/MergedAllocationStateMap.java | 22 ++++++------ .../verifier/PreRegisterAllocationPhase.java | 4 +-- .../lir/alloc/verifier/RAVInstruction.java | 34 +++++++++---------- .../RegisterAllocationVerifierPhase.java | 8 ++--- 11 files changed, 103 insertions(+), 97 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 3ed492c53beb..f1fbf3332faa 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -1,14 +1,15 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.util.EconomicHashSet; + import java.util.Arrays; -import java.util.HashSet; import java.util.Set; public class ConflictedAllocationState extends AllocationState { protected Set conflictedStates; public ConflictedAllocationState() { - this.conflictedStates = new HashSet<>(); + this.conflictedStates = new EconomicHashSet<>(); } public ConflictedAllocationState(ValueAllocationState state1, ValueAllocationState state2) { @@ -18,7 +19,7 @@ public ConflictedAllocationState(ValueAllocationState state1, ValueAllocationSta } private ConflictedAllocationState(Set conflictedStates) { - this.conflictedStates = new HashSet<>(conflictedStates); + this.conflictedStates = new EconomicHashSet<>(conflictedStates); } public void addConflictedValue(ValueAllocationState state) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 618102554ab3..6a96aae998ad 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -8,11 +8,11 @@ import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.Value; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -22,8 +22,8 @@ public class ConstantMaterializationConflictResolver implements ConflictResolver protected Set canRematerializeToStack; public ConstantMaterializationConflictResolver() { - this.constantVariableMap = new HashMap<>(); - this.canRematerializeToStack = new HashSet<>(); + this.constantVariableMap = new EconomicHashMap<>(); + this.canRematerializeToStack = new EconomicHashSet<>(); } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java index 72d662386e2a..6996d8698c8e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java @@ -1,11 +1,11 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -14,13 +14,13 @@ public class DefinitionSet { protected Map valueMap; public DefinitionSet() { - this.internalSet = new HashSet<>(); - this.valueMap = new HashMap<>(); + this.internalSet = new EconomicHashSet<>(); + this.valueMap = new EconomicHashMap<>(); } public DefinitionSet(DefinitionSet defSet) { - this.internalSet = new HashSet<>(defSet.internalSet); - this.valueMap = new HashMap<>(defSet.valueMap); + this.internalSet = new EconomicHashSet<>(defSet.internalSet); + this.valueMap = new EconomicHashMap<>(defSet.valueMap); } public void add(Value value) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index 8842f9260aad..0a71ae2b5c3b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -6,12 +6,12 @@ import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -111,14 +111,14 @@ class VariableLocations implements Iterable { protected Set internalList; protected Map valueMap; - public VariableLocations() { - this.internalList = new HashSet<>(); - this.valueMap = new HashMap<>(); + VariableLocations() { + this.internalList = new EconomicHashSet<>(); + this.valueMap = new EconomicHashMap<>(); } - public VariableLocations(VariableLocations other) { - this.internalList = new HashSet<>(other.internalList); - this.valueMap = new HashMap<>(other.valueMap); + VariableLocations(VariableLocations other) { + this.internalList = new EconomicHashSet<>(other.internalList); + this.valueMap = new EconomicHashMap<>(other.valueMap); } public void add(Value location) { @@ -160,10 +160,10 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); // Definition block needs to have this set. - var propagateMap = new HashMap, List>(); - var locationMap = new HashMap, Map>(); + var propagateMap = new EconomicHashMap, List>(); + var locationMap = new EconomicHashMap, Map>(); - var defVariableToLocations = new HashMap(); + var defVariableToLocations = new EconomicHashMap(); var defBlockVariablesToPropagate = new ArrayList(); for (int i = 0; i < labelInstr.dests.count; i++) { var register = labelInstr.dests.curr[i]; @@ -177,7 +177,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { } Queue> worklist = new LinkedList<>(); - Set> processed = new HashSet<>(); + Set> processed = new EconomicHashSet<>(); worklist.add(defBlock); propagateMap.put(defBlock, defBlockVariablesToPropagate); locationMap.put(defBlock, defVariableToLocations); @@ -302,7 +302,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { continue; } - Map newLoc = new HashMap<>(); + Map newLoc = new EconomicHashMap<>(); var itToBePropagated = variablesToBePropagated.iterator(); while (itToBePropagated.hasNext()) { var variable = LIRValueUtil.asVariable(itToBePropagated.next()); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java index a54bbee87564..3b792a62590c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -2,16 +2,15 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.meta.Value; -import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -30,12 +29,12 @@ class PathEntry { public BasicBlock block; public PathEntry previous; - public PathEntry(BasicBlock block) { + PathEntry(BasicBlock block) { this.block = block; this.previous = null; } - public PathEntry(BasicBlock block, PathEntry previous) { + PathEntry(BasicBlock block, PathEntry previous) { this.block = block; this.previous = previous; } @@ -46,10 +45,15 @@ public PathEntry next(BasicBlock block) { @Override public boolean equals(Object o) { - if (this == o) return true; + if (this == o) { + return true; + } if (o instanceof PathEntry le && le.block.equals(this.block)) { - if (le.previous == null) return this.previous == null; + if (le.previous == null) { + return this.previous == null; + } + return le.previous.block.equals(this.previous.block); } @@ -71,14 +75,15 @@ protected FromUsageResolver(LIR lir, BlockMap> blockIn this.lir = lir; this.blockInstructions = blockInstructions; - this.labelMap = new HashMap<>(); - this.variableRegisterMap = new HashMap<>(); - this.usageAliasMap = new HashMap<>(); + this.labelMap = new EconomicHashMap<>(); + this.variableRegisterMap = new EconomicHashMap<>(); + this.usageAliasMap = new EconomicHashMap<>(); } private Value getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVInstruction.Base usageInstruction, Value location, Variable variable) { boolean reachedUsage = false; - while (true) { + while (true) { // TERMINATION ARGUMENT: Iterates over linked list using PathEntry nodes + // Either we get an assertion about path being null or reaching def block - which always happens assert path != null; var block = path.block; @@ -129,9 +134,8 @@ private void mapLabelVariableFromUsage( // @formatter:off Map> labelToBlockMap, Variable variable, Value location, - PathEntry path, RAVInstruction.Base useInstruction) + PathEntry path, RAVInstruction.Base useInstruction) { // @formatter:on - { var variableLabelInstr = this.labelMap.get(variable); if (variableLabelInstr == null) { return; @@ -211,9 +215,9 @@ public void resolvePhiFromUsage() { Queue worklist = new LinkedList<>(); worklist.add(new PathEntry(this.lir.getControlFlowGraph().getStartBlock())); - Map> labelToBlockMap = new HashMap<>(); + Map> labelToBlockMap = new EconomicHashMap<>(); - Set visited = new HashSet<>(); // Ignore already visited combinations. + Set visited = new EconomicHashSet<>(); // Ignore already visited combinations. while (!worklist.isEmpty()) { var entry = worklist.poll(); if (visited.contains(entry)) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 5fb1595df1ec..626fe28c57f4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -6,11 +6,11 @@ import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.meta.Value; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -35,18 +35,18 @@ class FromUsageResolverGlobal { public BlockMap blockUsageMap; // Entry blocks! public List> endBlocks; - class BlockUsage { + final class BlockUsage { private final DefinitionSet reached; private final Map locations; private BlockUsage() { this.reached = new DefinitionSet(); - this.locations = new HashMap<>(); + this.locations = new EconomicHashMap<>(); } private BlockUsage(BlockUsage blockDefs) { this.reached = new DefinitionSet(blockDefs.reached); - this.locations = new HashMap<>(blockDefs.locations); + this.locations = new EconomicHashMap<>(blockDefs.locations); } private BlockUsage merge(BlockUsage other) { @@ -76,13 +76,13 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b this.lir = lir; this.blockInstructions = blockInstructions; - this.labelMap = new HashMap<>(); - this.defined = new HashMap<>(); - this.reached = new HashMap<>(); - this.firstUsages = new HashMap<>(); - this.initialLocations = new HashMap<>(); - this.aliasMap = new HashMap<>(); - this.aliasBlockMap = new HashMap<>(); + this.labelMap = new EconomicHashMap<>(); + this.defined = new EconomicHashMap<>(); + this.reached = new EconomicHashMap<>(); + this.firstUsages = new EconomicHashMap<>(); + this.initialLocations = new EconomicHashMap<>(); + this.aliasMap = new EconomicHashMap<>(); + this.aliasBlockMap = new EconomicHashMap<>(); this.endBlocks = new ArrayList<>(); this.blockUsageMap = new BlockMap<>(lir.getControlFlowGraph()); @@ -108,7 +108,8 @@ public void resolvePhiFromUsage() { var instructions = blockInstructions.get(block); for (var instruction : instructions.reversed()) { switch (instruction) { - case RAVInstruction.VirtualMove ignored -> {} + case RAVInstruction.VirtualMove ignored -> { + } case RAVInstruction.Move move -> handleMove(usage, move.from, move.to); case RAVInstruction.Op op -> { if (op.lirInstruction instanceof StandardOp.LabelOp) { @@ -159,7 +160,7 @@ protected void initializeUsages() { worklist.add(startBlock); // Calculate what is defined when + usages - Set> visited = new HashSet<>(); + Set> visited = new EconomicHashSet<>(); while (!worklist.isEmpty()) { var block = worklist.remove(); if (visited.contains(block)) { @@ -235,7 +236,7 @@ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction reached.put(variable, false); if (!firstUsages.containsKey(op)) { - firstUsages.put(op, new HashSet<>()); + firstUsages.put(op, new EconomicHashSet<>()); } firstUsages.get(op).add(variable); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java index 0451a2792080..98bd61e168a1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java @@ -4,11 +4,11 @@ import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.meta.Value; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -22,9 +22,9 @@ public class LabelConflictResolver implements ConflictResolver { protected Map> expandedRules; public LabelConflictResolver() { - this.labelMap = new HashMap<>(); - this.rules = new HashMap<>(); - this.expandedRules = new HashMap<>(); + this.labelMap = new EconomicHashMap<>(); + this.rules = new EconomicHashMap<>(); + this.expandedRules = new EconomicHashMap<>(); } @Override @@ -66,7 +66,7 @@ protected void buildRules() { } var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - Set resolutions = new HashSet<>(); + Set resolutions = new EconomicHashSet<>(); for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); @@ -106,7 +106,7 @@ protected void expandRules() { } } - expandedRules.put(variable, new HashSet<>(sources)); + expandedRules.put(variable, new EconomicHashSet<>(sources)); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index 275ddc206e78..90f16eafe976 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -1,13 +1,13 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -40,20 +40,20 @@ public class MergedAllocationStateMap { protected RegisterAllocationConfig registerAllocationConfig; public MergedAllocationStateMap(RegisterAllocationConfig registerAllocationConfig) { - valueMap = new HashMap<>(); - internalMap = new HashMap<>(); - locationTimings = new HashMap<>(); - prioritizedLocations = new HashMap<>(); + valueMap = new EconomicHashMap<>(); + internalMap = new EconomicHashMap<>(); + locationTimings = new EconomicHashMap<>(); + prioritizedLocations = new EconomicHashMap<>(); time = 0; this.registerAllocationConfig = registerAllocationConfig; } public MergedAllocationStateMap(MergedAllocationStateMap other) { - valueMap = new HashMap<>(other.valueMap); - internalMap = new HashMap<>(other.internalMap); - locationTimings = new HashMap<>(other.locationTimings); - prioritizedLocations = new HashMap<>(other.prioritizedLocations); + valueMap = new EconomicHashMap<>(other.valueMap); + internalMap = new EconomicHashMap<>(other.internalMap); + locationTimings = new EconomicHashMap<>(other.locationTimings); + prioritizedLocations = new EconomicHashMap<>(other.prioritizedLocations); time = other.time + 1; registerAllocationConfig = other.registerAllocationConfig; @@ -132,7 +132,7 @@ public int getKeyTime(Value key) { } public Set getValueLocations(Value value) { - Set locations = new HashSet<>(); + Set locations = new EconomicHashSet<>(); for (var entry : this.internalMap.entrySet()) { if (entry.getValue() instanceof ValueAllocationState valState) { if (valState.getValue().equals(value)) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 74cfda05f681..7fd450424c8a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -14,6 +14,7 @@ import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.gen.LIRGenerator; import jdk.graal.compiler.lir.phases.AllocationPhase; +import jdk.graal.compiler.util.EconomicHashMap; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; @@ -23,7 +24,6 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EnumSet; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -98,7 +98,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo var preallocMap = state.createInstructionMap(lirGenRes); LIR lir = lirGenRes.getLIR(); - Map constantValueMap = new HashMap<>(); + Map constantValueMap = new EconomicHashMap<>(); var constOverwriteProc = new ConstantOverrideValueProcedure(lir, constantValueMap); for (var blockId : lir.getBlocks()) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 3907959072fd..18acaa45b740 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -47,23 +47,6 @@ public List getSpeculativeMoveList() { } } - private static class GetCountProcedure implements InstructionValueProcedure { - protected int count = 0; - - public int getCount() { - int count = this.count; - // Reset the count and go again for different argument type - this.count = 0; - return count; - } - - @Override - public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - count++; - return value; - } - } - public static class ValueArrayPair { protected Value[] orig; protected Value[] curr; @@ -168,6 +151,23 @@ public static class Op extends Base { public JavaKind[] kinds; public ValueArrayPair stateValues; + private final class GetCountProcedure implements InstructionValueProcedure { + private int count = 0; + + public int getCount() { + int count = this.count; + // Reset the count and go again for different argument type + this.count = 0; + return count; + } + + @Override + public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + count++; + return value; + } + } + public Op(LIRInstruction instruction) { super(instruction); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 731f5e02c553..f20aabf0bc51 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -15,6 +15,8 @@ import jdk.graal.compiler.options.Option; import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionType; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; @@ -22,8 +24,6 @@ import java.io.FileNotFoundException; import java.io.PrintStream; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -91,8 +91,8 @@ protected BlockMap> getVerifierInstructions(LIRGenerat var lir = lirGenRes.getLIR(); var preallocMap = state.getInstructionMap(lirGenRes); - Map definedVariables = new HashMap<>(); - Set presentInstructions = new HashSet<>(); + Map definedVariables = new EconomicHashMap<>(); + Set presentInstructions = new EconomicHashSet<>(); for (var blockId : lir.getBlocks()) { BasicBlock block = lir.getBlockById(blockId); ArrayList instructions = lir.getLIRforBlock(block); From 88e7d1ee856cf35cf3fe444513a794b96ee9b87b Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 14 Feb 2026 20:36:54 +0100 Subject: [PATCH 045/112] Format code --- .../lir/alloc/verifier/CircularDefinitionError.java | 2 +- .../compiler/lir/alloc/verifier/ConflictResolver.java | 2 ++ .../compiler/lir/alloc/verifier/FromUsageResolver.java | 2 +- .../graal/compiler/lir/alloc/verifier/PhiResolution.java | 7 +------ .../lir/alloc/verifier/PreRegisterAllocationPhase.java | 6 +++--- .../alloc/verifier/RegisterAllocationVerifierPhase.java | 2 +- .../lir/alloc/verifier/ValueNotInRegisterException.java | 2 ++ 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java index ea3ed7fa5ed9..1178ae3aa8e2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java @@ -29,7 +29,7 @@ static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, operandString.append(variable.toString()); if (location != null) { - operandString.append(" -> ").append(location.toString()); + operandString.append(" -> ").append(location); } else { operandString.append(" -> ?"); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index 018dfce78fd0..7199c02fbc9e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -9,6 +9,8 @@ public interface ConflictResolver { void prepare(LIR lir, BlockMap> blockInstructions); + ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState, Value location); + ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java index 3b792a62590c..9b117ec665e5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -102,7 +102,7 @@ private Value getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVIn // looking for any changes to the target register that could highlight // different register is supposed to be used, in case of reload/spill combo or // register move. If we are wrong about the target register then, it will - // get thrown out in the verification stage. TODO: maybe try to use multiple usages to be sure? + // get thrown out in the verification stage. switch (instruction) { case RAVInstruction.VirtualMove move -> { if (move.location.equals(location) && !move.variableOrConstant.equals(variable)) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java index a390e68798ef..6d122c90b225 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java @@ -1,10 +1,5 @@ package jdk.graal.compiler.lir.alloc.verifier; public enum PhiResolution { - FromJump, - FromUsage, - FromPredecessors, - FromUsageGlobal, - FromConflicts, - ByAllocator + FromJump, FromUsage, FromPredecessors, FromUsageGlobal, FromConflicts, ByAllocator } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 7fd450424c8a..47f2d083c089 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -37,10 +37,10 @@ public PreRegisterAllocationPhase(RegisterAllocationVerifierPhaseState state) { } public static class ConstantOverrideValueProcedure implements ValueProcedure { - private LIR lir; + private final LIR lir; private List variables; - private Map constantValueMap; - private Method nextVariableMethod; + private final Map constantValueMap; + private final Method nextVariableMethod; public ConstantOverrideValueProcedure(LIR lir, Map constantValueMap) { this.lir = lir; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index f20aabf0bc51..25da8ce4d16d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -44,7 +44,7 @@ public static class Options { public static final OptionKey RAFilter = new OptionKey<>(null); } - private RegisterAllocationVerifierPhaseState state; + private final RegisterAllocationVerifierPhaseState state; public RegisterAllocationVerifierPhase(RegisterAllocationVerifierPhaseState state) { this.state = state; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 3d0628e2c865..f3bfe50855ca 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -24,6 +24,7 @@ public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock blo static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { var messageBuilder = new StringBuilder(); + // @formatter:off messageBuilder .append("Value ") .append(variable) @@ -34,6 +35,7 @@ static String getErrorMessage(LIRInstruction instruction, BasicBlock block, V .append(" in ") .append(block) .append(" actual state is "); + // @formatter:on if (state instanceof ConflictedAllocationState confState) { var confStates = confState.getConflictedStates(); From ac00e5e9c9bee0a671b40fb353b452eae74445c0 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sun, 15 Feb 2026 19:13:16 +0100 Subject: [PATCH 046/112] Wrap Value class for data structure indexing --- .../AliveConstraintViolationException.java | 4 +- .../verifier/CircularDefinitionError.java | 8 +- .../lir/alloc/verifier/ConflictResolver.java | 4 +- ...nstantMaterializationConflictResolver.java | 37 ++++---- .../lir/alloc/verifier/DefinitionSet.java | 52 ---------- .../lir/alloc/verifier/FromJumpResolver.java | 6 +- .../verifier/FromPredecessorsResolver.java | 87 ++++------------- .../lir/alloc/verifier/FromUsageResolver.java | 32 +++---- .../verifier/FromUsageResolverGlobal.java | 71 +++++++------- .../verifier/KindsMismatchException.java | 12 +-- .../alloc/verifier/LabelConflictResolver.java | 39 ++++---- .../verifier/MergedAllocationStateMap.java | 94 ++++++------------- .../verifier/MergedBlockVerifierState.java | 66 +++++++------ .../alloc/verifier/MissingLocationError.java | 4 +- .../lir/alloc/verifier/RAVInstruction.java | 66 ++++++------- .../compiler/lir/alloc/verifier/RAValue.java | 68 ++++++++++++++ .../lir/alloc/verifier/RAVariable.java | 45 +++++++++ .../RegisterAllocationVerifierPhase.java | 10 +- .../alloc/verifier/ValueAllocationState.java | 17 ++-- .../verifier/ValueNotInRegisterException.java | 10 +- 20 files changed, 358 insertions(+), 374 deletions(-) delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index c0c4745c6fa6..91b18aecbfba 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -9,13 +9,13 @@ public class AliveConstraintViolationException extends RAVException { public LIRInstruction instruction; public BasicBlock block; - public AliveConstraintViolationException(LIRInstruction instruction, BasicBlock block, Value location, boolean asDest) { + public AliveConstraintViolationException(LIRInstruction instruction, BasicBlock block, RAValue location, boolean asDest) { super(AliveConstraintViolationException.getErrorMessage(instruction, block, location, asDest)); this.instruction = instruction; this.block = block; } - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value location, boolean asDest) { + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, RAValue location, boolean asDest) { if (asDest) { return "Location " + location + " used as both alive and output in " + instruction + " in block" + block; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java index 1178ae3aa8e2..7382367c2f65 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java @@ -8,19 +8,19 @@ @SuppressWarnings("serial") public class CircularDefinitionError extends RAVError { - public CircularDefinitionError(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { + public CircularDefinitionError(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { super(getErrorMessage(defBlock, predecessor, label, beingPropagated)); } - static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { + static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { StringBuilder operandString = new StringBuilder("["); var values = label.dests; for (int i = 0; i < values.count; i++) { - if (!LIRValueUtil.isVariable(values.orig[i])) { + if (!values.orig[i].isVariable()) { continue; // Avoid fatal error } - var variable = LIRValueUtil.asVariable(values.orig[i]); + var variable = values.orig[i].asVariable(); if (!beingPropagated.contains(variable)) { continue; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index 7199c02fbc9e..b8d83424871c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -10,7 +10,7 @@ public interface ConflictResolver { void prepare(LIR lir, BlockMap> blockInstructions); - ValueAllocationState resolveValueState(Variable target, ValueAllocationState valueState, Value location); + ValueAllocationState resolveValueState(RAVariable target, ValueAllocationState valueState, RAValue location); - ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location); + ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 6a96aae998ad..26c86521e85f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -6,20 +6,18 @@ import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.meta.Value; import java.util.List; import java.util.Map; import java.util.Set; public class ConstantMaterializationConflictResolver implements ConflictResolver { - protected Map constantVariableMap; - protected Set canRematerializeToStack; + protected Map constantVariableMap; + protected Set canRematerializeToStack; public ConstantMaterializationConflictResolver() { this.constantVariableMap = new EconomicHashMap<>(); @@ -47,12 +45,12 @@ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock bloc // MOVE rax, vstack:1 // USE v1 - if (!LIRValueUtil.isVariable(op.dests.orig[0])) { + if (!op.dests.orig[0].isVariable()) { return; } - var variable = LIRValueUtil.asVariable(op.dests.orig[0]); - var constantValue = new ConstantValue(variable.getValueKind(), loadConstantOp.getConstant()); + var variable = op.dests.orig[0].asVariable(); + var constantValue = new ConstantValue(variable.getValue().getValueKind(), loadConstantOp.getConstant()); constantVariableMap.put(variable, constantValue); if (loadConstantOp.canRematerializeToStack()) { @@ -62,26 +60,26 @@ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock bloc } @Override - public ValueAllocationState resolveConflictedState(Variable target, ConflictedAllocationState conflictedState, Value location) { + public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { var confStates = conflictedState.getConflictedStates(); - Variable variable = null; - ConstantValue constantValue = null; + RAVariable variable = null; + RAValue constantValue = null; for (var states : confStates) { - var value = states.getValue(); - if (LIRValueUtil.isVariable(value)) { + var value = states.getRAValue(); + if (value.isVariable()) { if (variable != null && !variable.equals(value)) { return null; } - variable = LIRValueUtil.asVariable(value); - } else if (value instanceof ConstantValue constValue) { - if (constantValue != null && !constantValue.equals(constValue)) { + variable = value.asVariable(); + } else if (value.getValue() instanceof ConstantValue) { + if (constantValue != null && !constantValue.equals(value)) { return null; } - constantValue = constValue; + constantValue = value; } } @@ -105,12 +103,12 @@ public ValueAllocationState resolveConflictedState(Variable target, ConflictedAl } @Override - public ValueAllocationState resolveValueState(Variable variable, ValueAllocationState valueState, Value location) { + public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocationState valueState, RAValue location) { if (!this.constantVariableMap.containsKey(variable)) { return null; } - if (valueState.getValue() instanceof ConstantValue constant) { + if (valueState.getRAValue().getValue() instanceof ConstantValue constant) { if (!this.constantVariableMap.get(variable).equals(constant)) { return null; } @@ -125,7 +123,8 @@ public ValueAllocationState resolveValueState(Variable variable, ValueAllocation return null; } - protected boolean isRematerializedToWrongLocation(Variable variable, Value location) { + protected boolean isRematerializedToWrongLocation(RAVariable variable, RAValue raLocation) { + var location = raLocation.getValue(); if (location instanceof StackSlot || location instanceof VirtualStackSlot) { return !canRematerializeToStack.contains(variable); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java deleted file mode 100644 index 6996d8698c8e..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/DefinitionSet.java +++ /dev/null @@ -1,52 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.util.EconomicHashMap; -import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.meta.Value; - -import java.util.Map; -import java.util.Set; - -public class DefinitionSet { - protected Set internalSet; - protected Map valueMap; - - public DefinitionSet() { - this.internalSet = new EconomicHashSet<>(); - this.valueMap = new EconomicHashMap<>(); - } - - public DefinitionSet(DefinitionSet defSet) { - this.internalSet = new EconomicHashSet<>(defSet.internalSet); - this.valueMap = new EconomicHashMap<>(defSet.valueMap); - } - - public void add(Value value) { - if (Value.ILLEGAL.equals(value)) { - return; - } - - String valueString = this.getValueKeyString(value); - this.valueMap.put(valueString, value); - this.internalSet.add(valueString); - } - - public boolean contains(Value value) { - String valueString = this.getValueKeyString(value); - return this.internalSet.contains(valueString); - } - - protected String getValueKeyString(Value value) { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); - } - - if (LIRValueUtil.isVariable(value)) { - return "v" + LIRValueUtil.asVariable(value).index; - } - - return value.toString(); - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java index 59a4cbbf6751..87a15da533fb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java @@ -22,7 +22,7 @@ public FromJumpResolver(LIR lir, BlockMap> blockInstru public void resolvePhi(BasicBlock block) { var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getFirst(); for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; + Set locations = null; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); var state = this.blockStates.get(pred); @@ -46,7 +46,7 @@ public void resolvePhi(BasicBlock block) { continue; } - Value location = null; + RAValue location = null; if (locations.size() != 1) { if (locations.isEmpty()) { return; @@ -54,7 +54,7 @@ public void resolvePhi(BasicBlock block) { for (int j = 0; j < block.getPredecessorCount(); j++) { int time = -1; - Value blockReg = null; + RAValue blockReg = null; for (var loc : locations) { var pred = block.getPredecessorAt(j); var state = this.blockStates.get(pred); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index 0a71ae2b5c3b..581eea4d069d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -46,7 +46,7 @@ public FromPredecessorsResolver(LIR lir, BlockMap> blo */ public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; + Set locations = null; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); var state = this.blockStates.get(pred); @@ -62,7 +62,7 @@ public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op locations.retainAll(varLoc); } - Value location = null; + RAValue location = null; if (locations.size() != 1) { if (locations.isEmpty()) { return false; @@ -70,7 +70,7 @@ public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op for (int j = 0; j < block.getPredecessorCount(); j++) { int time = -1; - Value blockReg = null; + RAValue blockReg = null; for (var loc : locations) { var pred = block.getPredecessorAt(j); var state = this.blockStates.get(pred); @@ -107,71 +107,22 @@ public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op return true; } - class VariableLocations implements Iterable { - protected Set internalList; - protected Map valueMap; - - VariableLocations() { - this.internalList = new EconomicHashSet<>(); - this.valueMap = new EconomicHashMap<>(); - } - - VariableLocations(VariableLocations other) { - this.internalList = new EconomicHashSet<>(other.internalList); - this.valueMap = new EconomicHashMap<>(other.valueMap); - } - - public void add(Value location) { - var locString = getValueKeyString(location); - internalList.add(locString); - valueMap.put(locString, location); - } - - public void remove(Value location) { - var locString = getValueKeyString(location); - internalList.remove(locString); - valueMap.remove(locString); - } - - public boolean isEmpty() { - return internalList.isEmpty(); - } - - public boolean contains(Value location) { - var locString = getValueKeyString(location); - return valueMap.containsKey(locString); - } - - protected String getValueKeyString(Value value) { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); - } - - return value.toString(); - } - - @Override - public Iterator iterator() { - return internalList.stream().map(valueMap::get).iterator(); - } - } - public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); // Definition block needs to have this set. - var propagateMap = new EconomicHashMap, List>(); - var locationMap = new EconomicHashMap, Map>(); + var propagateMap = new EconomicHashMap, List>(); + var locationMap = new EconomicHashMap, Map>>(); - var defVariableToLocations = new EconomicHashMap(); - var defBlockVariablesToPropagate = new ArrayList(); + var defVariableToLocations = new EconomicHashMap>(); + var defBlockVariablesToPropagate = new ArrayList(); for (int i = 0; i < labelInstr.dests.count; i++) { var register = labelInstr.dests.curr[i]; - var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + var variable = labelInstr.dests.orig[i].asVariable(); defBlockVariablesToPropagate.add(variable); - var variableLocationList = new VariableLocations(); + var variableLocationList = new EconomicHashSet(); variableLocationList.add(register); defVariableToLocations.put(variable, variableLocationList); } @@ -199,8 +150,8 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { continue; } - Value fromLocation; - Value toLocation; + RAValue fromLocation; + RAValue toLocation; switch (instruction) { case RAVInstruction.VirtualMove virtMove -> { @@ -220,7 +171,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var itToPropagate = variablesToPropagate.iterator(); while (itToPropagate.hasNext()) { - var variable = LIRValueUtil.asVariable(itToPropagate.next()); + var variable = itToPropagate.next(); var locations = variableToLocations.get(variable); locations.remove(location); } @@ -235,7 +186,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var itToPropagate = variablesToPropagate.iterator(); while (itToPropagate.hasNext()) { - var variable = LIRValueUtil.asVariable(itToPropagate.next()); + var variable = itToPropagate.next(); var locations = variableToLocations.get(variable); if (fromLocation != null && locations.contains(fromLocation)) { locations.add(toLocation); @@ -245,10 +196,10 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { } } - var variablesToBePropagated = new ArrayList(); + var variablesToBePropagated = new ArrayList(); var iterator = variablesToPropagate.iterator(); while (iterator.hasNext()) { - var variable = LIRValueUtil.asVariable(iterator.next()); + var variable = iterator.next(); var locations = variableToLocations.get(variable); if (locations.isEmpty()) { continue; @@ -280,7 +231,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { // entry state for B0 to include v0, which is defined by B0. for (int j = 0; j < labelInstr.dests.count; j++) { - var variable = LIRValueUtil.asVariable(labelInstr.dests.orig[j]); + var variable = labelInstr.dests.orig[j]; if (!variablesToPropagate.contains(variable)) { continue; @@ -302,16 +253,16 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { continue; } - Map newLoc = new EconomicHashMap<>(); + Map> newLoc = new EconomicHashMap<>(); var itToBePropagated = variablesToBePropagated.iterator(); while (itToBePropagated.hasNext()) { - var variable = LIRValueUtil.asVariable(itToBePropagated.next()); + var variable = itToBePropagated.next(); var locations = variableToLocations.get(variable); for (var location : locations) { succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr)); } - newLoc.put(variable, new VariableLocations(locations)); + newLoc.put(variable, new EconomicHashSet<>(locations)); } locationMap.put(succ, newLoc); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java index 9b117ec665e5..b4e001de8023 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -21,9 +21,9 @@ class FromUsageResolver { protected LIR lir; protected BlockMap> blockInstructions; - public Map labelMap; - public Map variableRegisterMap; - public Map usageAliasMap; + public Map labelMap; + public Map variableRegisterMap; + public Map usageAliasMap; class PathEntry { public BasicBlock block; @@ -80,7 +80,7 @@ protected FromUsageResolver(LIR lir, BlockMap> blockIn this.usageAliasMap = new EconomicHashMap<>(); } - private Value getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVInstruction.Base usageInstruction, Value location, Variable variable) { + private RAValue getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVInstruction.Base usageInstruction, RAValue location, RAValue variable) { boolean reachedUsage = false; while (true) { // TERMINATION ARGUMENT: Iterates over linked list using PathEntry nodes // Either we get an assertion about path being null or reaching def block - which always happens @@ -133,7 +133,7 @@ private Value getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVIn private void mapLabelVariableFromUsage( // @formatter:off Map> labelToBlockMap, - Variable variable, Value location, + RAVariable variable, RAValue location, PathEntry path, RAVInstruction.Base useInstruction) { // @formatter:on var variableLabelInstr = this.labelMap.get(variable); @@ -162,12 +162,12 @@ private void mapLabelVariableFromUsage( } for (var aliasEntry : this.usageAliasMap.entrySet()) { - var originalVariable = LIRValueUtil.asVariable(aliasEntry.getValue()); + var originalVariable = aliasEntry.getValue(); if (!originalVariable.equals(variable)) { continue; } - var aliasVariable = LIRValueUtil.asVariable(aliasEntry.getKey()); + var aliasVariable = aliasEntry.getKey(); var aliasLabelInstr = this.labelMap.get(aliasVariable); if (aliasLabelInstr == null) { continue; @@ -193,19 +193,19 @@ private void mapLabelVariableFromUsage( private void tryMappingVariable( // @formatter:off - Map> labelToBlockMap, Value variable, - Value location, PathEntry path, RAVInstruction.Base useInstruction + Map> labelToBlockMap, RAValue raVariable, + RAValue raLocation, PathEntry path, RAVInstruction.Base useInstruction // @formatter:on ) { - if (!LIRValueUtil.isVariable(variable)) { + if (!raVariable.isVariable()) { return; } - if (LIRValueUtil.isVariable(location) || location instanceof ConstantValue) { + if (raLocation.isVariable() || raLocation.getValue() instanceof ConstantValue) { return; } - this.mapLabelVariableFromUsage(labelToBlockMap, LIRValueUtil.asVariable(variable), location, path, useInstruction); + this.mapLabelVariableFromUsage(labelToBlockMap, raVariable.asVariable(), raLocation, path, useInstruction); } /** @@ -230,7 +230,7 @@ public void resolvePhiFromUsage() { var labelInstr = (RAVInstruction.Op) instructions.getFirst(); for (int i = 0; i < labelInstr.dests.count; i++) { if (labelInstr.dests.curr[i] == null) { - Variable variable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); + RAVariable variable = labelInstr.dests.orig[i].asVariable(); this.labelMap.put(variable, labelInstr); labelToBlockMap.put(labelInstr, block); } @@ -248,13 +248,13 @@ public void resolvePhiFromUsage() { // so in-case an alias was defined after that happened, it's not resolved and will fail. var label = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); for (int i = 0; i < op.alive.count; i++) { - if (!LIRValueUtil.isVariable(op.alive.orig[i])) { + if (!op.alive.orig[i].isVariable()) { continue; } - var variable = LIRValueUtil.asVariable(op.alive.orig[i]); + var variable = op.alive.orig[i].asVariable(); if (this.labelMap.get(variable) != null) { - this.usageAliasMap.put(variable, LIRValueUtil.asVariable(label.dests.orig[i])); + this.usageAliasMap.put(variable, label.dests.orig[i].asVariable()); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 626fe28c57f4..5fbb938bfbd5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -25,43 +25,45 @@ class FromUsageResolverGlobal { protected LIR lir; protected BlockMap> blockInstructions; - public Map labelMap; - public Map defined; - public Map reached; - public Map> firstUsages; - public Map initialLocations; - public Map aliasMap; - public Map> aliasBlockMap; + public Map labelMap; + public Map defined; + public Map reached; + public Map> firstUsages; + public Map initialLocations; + public Map aliasMap; + public Map> aliasBlockMap; public BlockMap blockUsageMap; // Entry blocks! public List> endBlocks; final class BlockUsage { - private final DefinitionSet reached; - private final Map locations; + private final Set reached; + private final Map locations; private BlockUsage() { - this.reached = new DefinitionSet(); + this.reached = new EconomicHashSet<>(); this.locations = new EconomicHashMap<>(); } private BlockUsage(BlockUsage blockDefs) { - this.reached = new DefinitionSet(blockDefs.reached); + this.reached = new EconomicHashSet<>(blockDefs.reached); this.locations = new EconomicHashMap<>(blockDefs.locations); } private BlockUsage merge(BlockUsage other) { var newDefs = new BlockUsage(this); - for (var defString : other.reached.internalSet) { - var defValue = other.reached.valueMap.get(defString); + for (var defValue : other.reached) { newDefs.reached.add(defValue); } var iterator = other.locations.keySet().iterator(); while (iterator.hasNext()) { - var variable = LIRValueUtil.asVariable(iterator.next()); + var variable = iterator.next(); var defValue = other.locations.get(variable); + if (defValue == null) { + continue; + } - if (Value.ILLEGAL.equals(defValue) && newDefs.locations.containsKey(variable)) { + if (defValue.isIllegal() && newDefs.locations.containsKey(variable)) { continue; } @@ -121,7 +123,7 @@ public void resolvePhiFromUsage() { if (firstUsages.containsKey(op)) { var iterator = firstUsages.get(op).iterator(); while (iterator.hasNext()) { - var variable = LIRValueUtil.asVariable(iterator.next()); + var variable = iterator.next(); usage.locations.put(variable, initialLocations.get(variable)); usage.reached.add(variable); } @@ -142,7 +144,7 @@ public void resolvePhiFromUsage() { var newReached = predReached.merge(usage); this.blockUsageMap.put(pred, newReached); - if (predReached.reached.internalSet.size() == newReached.reached.internalSet.size()) { + if (predReached.reached.size() == newReached.reached.size()) { continue; } } @@ -173,8 +175,8 @@ protected void initializeUsages() { var label = (RAVInstruction.Op) instructions.getFirst(); for (var i = 0; i < label.dests.count; i++) { - if (LIRValueUtil.isVariable(label.dests.orig[i])) { - var variable = LIRValueUtil.asVariable(label.dests.orig[i]); + if (label.dests.orig[i].isVariable()) { + var variable = label.dests.orig[i].asVariable(); defined.put(variable, true); labelMap.put(variable, label); } @@ -194,18 +196,18 @@ protected void initializeUsages() { var jump = (RAVInstruction.Op) instructions.getLast(); for (var i = 0; i < jump.alive.count; i++) { - if (!LIRValueUtil.isVariable(jump.alive.orig[i])) { + if (!jump.alive.orig[i].isVariable()) { continue; } - var variable = LIRValueUtil.asVariable(jump.alive.orig[i]); + var variable = jump.alive.orig[i].asVariable(); if (defined.containsKey(variable) && !reached.containsKey(variable)) { // No usage found before this jump var succ = block.getSuccessorAt(0); var succLabel = (RAVInstruction.Op) blockInstructions.get(succ).getFirst(); aliasBlockMap.put(variable, block); - aliasMap.put(variable, LIRValueUtil.asVariable(succLabel.dests.orig[i])); + aliasMap.put(variable, succLabel.dests.orig[i].asVariable()); } } @@ -225,11 +227,11 @@ protected void initializeUsages() { protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { for (var i = 0; i < values.count; i++) { - if (!LIRValueUtil.isVariable(values.orig[i])) { + if (!values.orig[i].isVariable()) { continue; } - var variable = LIRValueUtil.asVariable(values.orig[i]); + var variable = values.orig[i].asVariable(); if (defined.containsKey(variable) && !reached.containsKey(variable)) { // Defined - variable comes from label // Reached does not contain variable - there's no other first usage. @@ -247,27 +249,30 @@ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction } } - protected void handleMove(BlockUsage usage, Value from, Value to) { + protected void handleMove(BlockUsage usage, RAValue from, RAValue to) { + var updatedVariables = new EconomicHashMap(); for (var entry : usage.locations.entrySet()) { - var variable = LIRValueUtil.asVariable(entry.getKey()); + var variable = entry.getKey(); var location = entry.getValue(); - if (location == null || Value.ILLEGAL.equals(location)) { + if (location == null || location.isIllegal()) { continue; } if (location.equals(to) && usage.reached.contains(variable)) { - usage.locations.put(variable, from); + updatedVariables.put(variable, from); } } + + usage.locations.putAll(updatedVariables); } protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBlock block) { for (int i = 0; i < label.dests.count; i++) { - if (!LIRValueUtil.isVariable(label.dests.orig[i])) { + if (!label.dests.orig[i].isVariable()) { continue; } - var variable = LIRValueUtil.asVariable(label.dests.orig[i]); + var variable = label.dests.orig[i].asVariable(); if (usage.locations.get(variable) == null) { continue; } @@ -277,7 +282,7 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc } var location = usage.locations.get(variable); - if (location == null || Value.ILLEGAL.equals(location)) { + if (location == null || location.isIllegal()) { throw new IllegalStateException(); } @@ -293,9 +298,9 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc // for same variable in successor label, whenever said variable is resolved // we now have a location for this variable and can take other moves into account. for (var entry : aliasMap.entrySet()) { - var aliased = LIRValueUtil.asVariable(entry.getValue()); + var aliased = entry.getValue(); if (variable.equals(aliased)) { - var alias = LIRValueUtil.asVariable(entry.getKey()); + var alias = entry.getKey(); var aliasBlock = aliasBlockMap.get(alias); if (blockUsageMap.get(aliasBlock) == null) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java index 8ad6ceb4c3c4..b5313d3fb77e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -8,11 +8,11 @@ public class KindsMismatchException extends RAVException { public LIRInstruction instruction; public BasicBlock block; - public Value value1; - public Value value2; + public RAValue value1; + public RAValue value2; public boolean origVsCurr; - public KindsMismatchException(LIRInstruction instruction, BasicBlock block, Value value1, Value value2, boolean origVsCurr) { + public KindsMismatchException(LIRInstruction instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { super(KindsMismatchException.getErrorMessage(instruction, block, value1, value2, origVsCurr)); this.instruction = instruction; @@ -22,11 +22,11 @@ public KindsMismatchException(LIRInstruction instruction, BasicBlock block, V this.origVsCurr = origVsCurr; } - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value value1, Value value2, boolean origVsCurr) { + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { if (origVsCurr) { - return value1.getValueKind() + " has different kind after allocation: " + value2.getValueKind() + " in " + instruction + " in block " + block; + return value1.getValue().getValueKind() + " has different kind after allocation: " + value2.getValue().getValueKind() + " in " + instruction + " in block " + block; } - return "Value in location has different kind: " + value1.getValueKind() + " vs. " + value2.getValueKind() + " in " + instruction + " in block " + block; + return "Value in location has different kind: " + value1.getValue().getValueKind() + " vs. " + value2.getValue().getValueKind() + " in " + instruction + " in block " + block; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java index 98bd61e168a1..2fafdca68aec 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java @@ -2,11 +2,8 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.meta.Value; import java.util.ArrayList; import java.util.List; @@ -17,9 +14,9 @@ public class LabelConflictResolver implements ConflictResolver { protected LIR lir; protected BlockMap> blockInstructions; - protected Map labelMap; - protected Map> rules; - protected Map> expandedRules; + protected Map labelMap; + protected Map> rules; + protected Map> expandedRules; public LabelConflictResolver() { this.labelMap = new EconomicHashMap<>(); @@ -44,12 +41,12 @@ protected void buildLabelMap() { var labelInstr = (RAVInstruction.Op) instructions.getFirst(); for (int i = 0; i < labelInstr.dests.count; i++) { - if (!LIRValueUtil.isVariable(labelInstr.dests.orig[i])) { + if (!labelInstr.dests.orig[i].isVariable()) { continue; } - var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - labelMap.put(labelVariable, labelInstr); + var labelVariable = labelInstr.dests.orig[i]; + labelMap.put(labelVariable.asVariable(), labelInstr); } } } @@ -61,12 +58,12 @@ protected void buildRules() { var labelInstr = (RAVInstruction.Op) instructions.getFirst(); for (int i = 0; i < labelInstr.dests.count; i++) { - if (!LIRValueUtil.isVariable(labelInstr.dests.orig[i])) { + if (!labelInstr.dests.orig[i].isVariable()) { continue; } - var labelVariable = LIRValueUtil.asVariable(labelInstr.dests.orig[i]); - Set resolutions = new EconomicHashSet<>(); + var labelVariable = labelInstr.dests.orig[i].asVariable(); + Set resolutions = new EconomicHashSet<>(); for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); @@ -83,17 +80,17 @@ protected void buildRules() { protected void expandRules() { for (var entry : rules.entrySet()) { - var variable = LIRValueUtil.asVariable(entry.getKey()); + var variable = entry.getKey(); - ArrayList sources = new ArrayList<>(entry.getValue()); + ArrayList sources = new ArrayList<>(entry.getValue()); int sourceCount = sources.size(); for (int i = 0; i < sourceCount; i++) { var source = sources.get(i); - if (!LIRValueUtil.isVariable(source)) { + if (!source.isVariable()) { continue; } - var sourceVariable = LIRValueUtil.asVariable(source); + var sourceVariable = source.asVariable(); if (rules.containsKey(sourceVariable)) { for (var newSource : rules.get(sourceVariable)) { if (sources.contains(newSource)) { @@ -110,9 +107,9 @@ protected void expandRules() { } } - protected boolean isSubsetOfValues(Set superset, Set subset) { + protected boolean isSubsetOfValues(Set superset, Set subset) { for (var v : subset) { - if (!superset.contains(v.getValue())) { + if (!superset.contains(v.getRAValue())) { return false; } } @@ -120,7 +117,7 @@ protected boolean isSubsetOfValues(Set superset, Set valueMap; - protected Map internalMap; - protected Map locationTimings; + protected Map internalMap; + protected Map locationTimings; /** * Prioritized locations are ones made by the register allocator itself. *

@@ -34,13 +30,12 @@ public class MergedAllocationStateMap { *

* If there's multiple, then time should make the difference. */ - protected Map prioritizedLocations; + protected Map prioritizedLocations; protected int time; protected RegisterAllocationConfig registerAllocationConfig; public MergedAllocationStateMap(RegisterAllocationConfig registerAllocationConfig) { - valueMap = new EconomicHashMap<>(); internalMap = new EconomicHashMap<>(); locationTimings = new EconomicHashMap<>(); prioritizedLocations = new EconomicHashMap<>(); @@ -50,7 +45,6 @@ public MergedAllocationStateMap(RegisterAllocationConfig registerAllocationConfi } public MergedAllocationStateMap(MergedAllocationStateMap other) { - valueMap = new EconomicHashMap<>(other.valueMap); internalMap = new EconomicHashMap<>(other.internalMap); locationTimings = new EconomicHashMap<>(other.locationTimings); prioritizedLocations = new EconomicHashMap<>(other.prioritizedLocations); @@ -59,49 +53,38 @@ public MergedAllocationStateMap(MergedAllocationStateMap other) { registerAllocationConfig = other.registerAllocationConfig; } - public AllocationState get(Value key) { + public AllocationState get(RAValue key) { return this.get(key, AllocationState.getDefault()); } - public AllocationState get(Value key, AllocationState defaultValue) { - String keyString = this.getValueKeyString(key); - var state = internalMap.get(keyString); + public AllocationState get(RAValue key, AllocationState defaultValue) { + var state = internalMap.get(key); if (state == null) { return defaultValue; } return state; } - public AllocationState get(Register register) { - return this.get(register.asValue()); - } - - public void putAsPrioritized(Value key, AllocationState value) { + public void putAsPrioritized(RAValue key, AllocationState value) { this.put(key, value); - this.prioritizedLocations.put(this.getValueKeyString(key), true); - } - - public void put(Register reg, AllocationState value) { - this.put(reg.asValue(), value); + this.prioritizedLocations.put(key, true); } - public void put(Value key, AllocationState state) { + public void put(RAValue key, AllocationState state) { this.checkRegisterDestinationValidity(key); putWithoutRegCheck(key, state); } - public void putWithoutRegCheck(Value key, AllocationState state) { - String keyString = this.getValueKeyString(key); - locationTimings.put(keyString, time++); - internalMap.put(keyString, state); - valueMap.put(keyString, key); + public void putWithoutRegCheck(RAValue key, AllocationState state) { + locationTimings.put(key, time++); + internalMap.put(key, state); - if (prioritizedLocations.containsKey(keyString)) { - prioritizedLocations.put(keyString, false); + if (prioritizedLocations.containsKey(key)) { + prioritizedLocations.put(key, false); } } - public void putClone(Value key, AllocationState value) { + public void putClone(RAValue key, AllocationState value) { if (value.isUnknown()) { this.put(key, value); return; @@ -110,35 +93,27 @@ public void putClone(Value key, AllocationState value) { this.put(key, value.clone()); } - public void putClone(Register reg, AllocationState value) { - this.put(reg.asValue(), value.clone()); - } - - public void putCloneAsPrioritized(Value key, AllocationState value) { + public void putCloneAsPrioritized(RAValue key, AllocationState value) { this.putClone(key, value); - this.prioritizedLocations.put(this.getValueKeyString(key), true); + this.prioritizedLocations.put(key, true); } - public boolean isPrioritized(Value key) { - String keyString = this.getValueKeyString(key); - return prioritizedLocations.containsKey(keyString); + public boolean isPrioritized(RAValue key) { + return prioritizedLocations.containsKey(key); } - public int getKeyTime(Value key) { - String keyString = this.getValueKeyString(key); - var time = locationTimings.get(keyString); - assert time != null : "Time for key " + keyString + " not present."; + public int getKeyTime(RAValue key) { + var time = locationTimings.get(key); + assert time != null : "Time for key " + key + " not present."; return time; } - public Set getValueLocations(Value value) { - Set locations = new EconomicHashSet<>(); + public Set getValueLocations(RAValue value) { + Set locations = new EconomicHashSet<>(); for (var entry : this.internalMap.entrySet()) { if (entry.getValue() instanceof ValueAllocationState valState) { - if (valState.getValue().equals(value)) { - var location = this.valueMap.get(entry.getKey()); - assert location != null : "Value not present in ValueMap: " + entry.getKey(); - locations.add(location); + if (valState.getRAValue().equals(value)) { + locations.add(entry.getKey()); } } } @@ -151,7 +126,7 @@ public boolean mergeWith(MergedAllocationStateMap source) { if (!this.internalMap.containsKey(entry.getKey())) { changed = true; - this.putWithoutRegCheck(source.valueMap.get(entry.getKey()), UnknownAllocationState.INSTANCE); + this.putWithoutRegCheck(entry.getKey(), UnknownAllocationState.INSTANCE); } var currentValue = this.internalMap.get(entry.getKey()); @@ -160,23 +135,14 @@ public boolean mergeWith(MergedAllocationStateMap source) { changed = true; } - this.putWithoutRegCheck(source.valueMap.get(entry.getKey()), result); + this.putWithoutRegCheck(entry.getKey(), result); } return changed; } - protected String getValueKeyString(Value value) { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); - } - - assert !Value.ILLEGAL.equals(value) : "Cannot use ILLEGAL as key in AllocationStateMap"; - - return value.toString(); - } - - protected void checkRegisterDestinationValidity(Value location) { + protected void checkRegisterDestinationValidity(RAValue raLocation) { + var location = raLocation.getValue(); if (!ValueUtil.isRegister(location)) { return; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index e71ee369f55d..cc47c061065c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -100,7 +100,7 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } if (state.isConflicted()) { - var variable = LIRValueUtil.asVariable(orig); + var variable = orig.asVariable(); var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); if (phiResolution == PhiResolution.FromConflicts && resolvedState == null) { @@ -120,12 +120,12 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } if (!valAllocState.value.equals(orig)) { - if (orig instanceof CastValue castValue && valAllocState.value.equals(castValue.underlyingValue())) { + if (orig.getValue() instanceof CastValue castValue && valAllocState.value.getValue().equals(castValue.underlyingValue())) { continue; // They aren't equal here because of the CastValue, so if they are equal afterwards, we skip next part. } - if (valAllocState.value instanceof ConstantValue) { - var variable = LIRValueUtil.asVariable(orig); + if (valAllocState.value.getValue() instanceof ConstantValue) { + var variable = orig.asVariable(); var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); if (resolvedState != null) { this.values.put(curr, resolvedState); @@ -133,8 +133,8 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } } - if (phiResolution == PhiResolution.FromConflicts && LIRValueUtil.isVariable(orig)) { - var variable = LIRValueUtil.asVariable(orig); + if (phiResolution == PhiResolution.FromConflicts && orig.isVariable()) { + var variable = orig.asVariable(); var resolvedState = labelConflictResolver.resolveValueState(variable, valAllocState, curr); if (resolvedState != null) { this.values.put(curr, resolvedState); @@ -152,31 +152,25 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } } - protected boolean kindsEqual(Value orig, Value curr) { - var origKind = orig.getValueKind(); - var currKind = curr.getValueKind(); + protected boolean kindsEqual(RAValue orig, RAValue curr) { + var origKind = orig.getValue().getValueKind(); + var currKind = curr.getValue().getValueKind(); - if (currKind.equals(origKind)) { - return true; + if (origKind instanceof LIRKindWithCast castKind) { + origKind = castKind.getActualKind(); } - if (origKind instanceof LIRKindWithCast || currKind instanceof LIRKindWithCast) { - // TestCase: BoxingTest.boxShort - // MOV (x: [v11|QWORD[.] + 12], y: reinterpret: v0|DWORD as: WORD) size: WORD - // MOV (x: [rax|QWORD[.] + 12], y: r10|WORD(DWORD)) size: WORD - return origKind.getPlatformKind().equals(currKind.getPlatformKind()); + if (currKind instanceof LIRKindWithCast castKind) { + currKind = castKind.getActualKind(); } - // TODO: maybe we should also look at the type before cast - // LIRKindWithCast.getActualKind() - // CastValue.underlyingValue().getValueKind() - return false; + return currKind.equals(origKind); } - protected boolean kindsEqualFromState(Value orig, Value fromState) { - ValueKind origKind = orig.getValueKind(); - ValueKind currKind = fromState.getValueKind(); - if (orig instanceof CastValue castOrig) { + protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { + ValueKind origKind = orig.getValue().getValueKind(); + ValueKind currKind = fromState.getValue().getValueKind(); + if (orig.getValue() instanceof CastValue castOrig) { origKind = castOrig.underlyingValue().getValueKind(); } @@ -185,8 +179,12 @@ protected boolean kindsEqualFromState(Value orig, Value fromState) { protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock block) { for (int i = 0; i < instruction.alive.count; i++) { - Value value = instruction.alive.curr[i]; - if (Value.ILLEGAL.equals(value)) { + RAValue value = instruction.alive.curr[i]; + if (value == null) { + continue; + } + + if (value.isIllegal()) { continue; // TODO: remove IllegalValues from these arrays. } @@ -227,7 +225,7 @@ public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstr var curr = op.stateValues.curr[i]; var state = this.values.get(curr); - if (state instanceof ValueAllocationState valueAllocationState && valueAllocationState.getValue().equals(orig)) { + if (state instanceof ValueAllocationState valueAllocationState && valueAllocationState.getRAValue().equals(orig)) { continue; } @@ -267,7 +265,7 @@ public void update(RAVInstruction.Base instruction, BasicBlock block) { protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { for (int i = 0; i < op.dests.count; i++) { - if (Value.ILLEGAL.equals(op.dests.orig[i])) { + if (op.dests.orig[i].isIllegal()) { continue; // Safe to ignore, when destination is illegal value, not when used. } @@ -281,8 +279,8 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { continue; } - Value location = op.dests.curr[i]; - Value variable = op.dests.orig[i]; + RAValue location = op.dests.curr[i]; + RAValue variable = op.dests.orig[i]; if (location.equals(variable)) { // Only check register validity if it was changed by the register allocator @@ -295,20 +293,20 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { for (int i = 0; i < op.temp.count; i++) { var value = op.temp.curr[i]; - if (Value.ILLEGAL.equals(value)) { + if (value.isIllegal()) { continue; } // We cannot believe the contents of registers used as temp, thus we need to reset. - Value location = op.temp.curr[i]; + RAValue location = op.temp.curr[i]; this.values.put(location, UnknownAllocationState.INSTANCE); } } protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { - if (virtMove.location instanceof RegisterValue) { + if (virtMove.location.getValue() instanceof RegisterValue) { this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); - } else if (LIRValueUtil.isVariable(virtMove.location)) { + } else if (virtMove.location.isVariable()) { // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD // Move before allocation // TODO: maybe handle this better than VirtualMove with location as Variable diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java index 4e3774c44423..cfe0b04112eb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java @@ -6,11 +6,11 @@ @SuppressWarnings("serial") public class MissingLocationError extends RAVError { - public MissingLocationError(LIRInstruction instruction, BasicBlock block, Value variable) { + public MissingLocationError(LIRInstruction instruction, BasicBlock block, RAValue variable) { super(MissingLocationError.getMessage(instruction, block, variable)); } - static String getMessage(LIRInstruction instruction, BasicBlock block, Value variable) { + static String getMessage(LIRInstruction instruction, BasicBlock block, RAValue variable) { return "Variable " + variable + " is missing a location in " + instruction + " in block " + block; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 18acaa45b740..c353ebb33e47 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -48,14 +48,14 @@ public List getSpeculativeMoveList() { } public static class ValueArrayPair { - protected Value[] orig; - protected Value[] curr; + protected RAValue[] orig; + protected RAValue[] curr; protected int count; public ValueArrayPair(int count) { this.count = count; - this.curr = new Value[count]; - this.orig = new Value[count]; + this.curr = new RAValue[count]; + this.orig = new RAValue[count]; } public InstructionValueProcedure copyOriginalProc = new InstructionValueProcedure() { @@ -80,12 +80,12 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope } }; - public Value getCurrent(int index) { + public RAValue getCurrent(int index) { assert index < this.count : "Index out of bounds"; return this.curr[index]; } - public Value getOrig(int index) { + public RAValue getOrig(int index) { assert index < this.count : "Index out of bounds"; return this.orig[index]; } @@ -98,12 +98,12 @@ public void addCurrent(int index, Value value) { return; } - this.curr[index] = value; + this.curr[index] = RAValue.create(value); } public void addOrig(int index, Value value) { assert index < this.orig.length : "Index out of bounds"; - this.orig[index] = value; + this.orig[index] = RAValue.create(value); } /** @@ -205,13 +205,13 @@ public String toString() { } public static class Move extends Base { - public Value from; - public Value to; + public RAValue from; + public RAValue to; public Move(LIRInstruction instr, Value from, Value to) { super(instr); - this.from = from; - this.to = to; + this.from = RAValue.create(from); + this.to = RAValue.create(to); } public String toString() { @@ -220,13 +220,15 @@ public String toString() { } public static class RegMove extends Move { - public RegisterValue from; - public RegisterValue to; + // public RegisterValue from; + // public RegisterValue to; + public RAValue from; + public RAValue to; public RegMove(LIRInstruction instr, RegisterValue from, RegisterValue to) { super(instr, from, to); - this.from = from; - this.to = to; + this.from = RAValue.create(from); + this.to = RAValue.create(to); } public String toString() { @@ -237,8 +239,8 @@ public String toString() { // StackMove class to handle STACKMOVE instruction, temporary for now // before I decide how all Moves should be handled + all possible combinations. public static class StackMove extends Move { - public Value from; - public Value to; + public RAValue from; + public RAValue to; public StackMove(LIRInstruction instr, Value from, Value to) { super(instr, from, to); @@ -246,8 +248,8 @@ public StackMove(LIRInstruction instr, Value from, Value to) { assert from instanceof StackSlot || from instanceof VirtualStackSlot : "StackMove needs to receive instanceof StackSlot or VirtualStackSlot"; assert to instanceof StackSlot || to instanceof VirtualStackSlot : "StackMove needs to receive instanceof StackSlot or VirtualStackSlot"; - this.from = from; - this.to = to; + this.from = RAValue.create(from); + this.to = RAValue.create(to); } public String toString() { @@ -256,13 +258,13 @@ public String toString() { } public static class Reload extends Move { - public Value from; - public RegisterValue to; + public RAValue from; + public RAValue to; public Reload(LIRInstruction instr, RegisterValue to, Value from) { super(instr, from, to); - this.from = from; - this.to = to; + this.from = RAValue.create(from); + this.to = RAValue.create(to); } public String toString() { @@ -271,13 +273,13 @@ public String toString() { } public static class Spill extends Move { - public Value to; - public RegisterValue from; + public RAValue from; + public RAValue to; public Spill(LIRInstruction instr, Value to, RegisterValue from) { super(instr, from, to); - this.to = to; - this.from = from; + this.from = RAValue.create(from); + this.to = RAValue.create(to); } public String toString() { @@ -286,13 +288,13 @@ public String toString() { } public static class VirtualMove extends Move { - public Value variableOrConstant; - public Value location; + public RAValue variableOrConstant; + public RAValue location; public VirtualMove(LIRInstruction instr, Value variableOrConstant, Value location) { super(instr, variableOrConstant, location); - this.variableOrConstant = variableOrConstant; - this.location = location; + this.variableOrConstant = RAValue.create(variableOrConstant); + this.location = RAValue.create(location); } public String toString() { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java new file mode 100644 index 000000000000..499966e70d98 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -0,0 +1,68 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.meta.Value; + +public class RAValue { + public static RAValue create(Value value) { + if (LIRValueUtil.isVariable(value)) { + return new RAVariable(LIRValueUtil.asVariable(value)); + } + + return new RAValue(value); + } + + protected Value value; + + protected RAValue(Value value) { + this.value = value; + } + + public Value getValue() { + return this.value; + } + + public boolean isIllegal() { + return Value.ILLEGAL.equals(value); + } + + public RAVariable asVariable() { + return (RAVariable) this; + } + + public boolean isVariable() { + return false; + } + + @Override + public int hashCode() { + if (this.value instanceof RegisterValue registerValue) { + return registerValue.getRegister().hashCode(); + } + + return this.value.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof RAValue otherValueWrap) { + if (this.value instanceof RegisterValue thisReg && otherValueWrap.value instanceof RegisterValue otherReg) { + return thisReg.getRegister().equals(otherReg.getRegister()); + } + + return this.value.equals(otherValueWrap.value); + } + + return false; + } + + @Override + public String toString() { + if (value instanceof RegisterValue regValue) { + return regValue.getRegister().toString(); + } + + return value.toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java new file mode 100644 index 000000000000..c39088590f84 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java @@ -0,0 +1,45 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +import jdk.graal.compiler.lir.Variable; + +public class RAVariable extends RAValue { + protected Variable variable; + + protected RAVariable(Variable variable) { + super(variable); + this.variable = variable; + } + + @Override + public RAVariable asVariable() { + return this; + } + + @Override + public boolean isVariable() { + return true; + } + + public Variable getVariable() { + return variable; + } + + @Override + public int hashCode() { + return variable.index; + } + + @Override + public boolean equals(Object other) { + if (other instanceof RAVariable raVariable) { + return variable.index == raVariable.variable.index; + } + + return false; + } + + @Override + public String toString() { + return "v" + variable.index; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 25da8ce4d16d..f8b5dc81130e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -91,7 +91,7 @@ protected BlockMap> getVerifierInstructions(LIRGenerat var lir = lirGenRes.getLIR(); var preallocMap = state.getInstructionMap(lirGenRes); - Map definedVariables = new EconomicHashMap<>(); + Map definedVariables = new EconomicHashMap<>(); Set presentInstructions = new EconomicHashSet<>(); for (var blockId : lir.getBlocks()) { BasicBlock block = lir.getBlockById(blockId); @@ -103,8 +103,8 @@ protected BlockMap> getVerifierInstructions(LIRGenerat var rAVInstr = preallocMap.get(instruction); if (rAVInstr instanceof RAVInstruction.Op op) { for (int i = 0; i < op.dests.count; i++) { - if (LIRValueUtil.isVariable(op.dests.orig[i])) { - var variable = LIRValueUtil.asVariable(op.dests.orig[i]); + if (op.dests.orig[i].isVariable()) { + var variable = op.dests.orig[i].asVariable(); definedVariables.put(variable, op); } } @@ -148,8 +148,8 @@ protected BlockMap> getVerifierInstructions(LIRGenerat continue; } - if (!LIRValueUtil.isVariable(speculativeMove.location) && LIRValueUtil.isVariable(speculativeMove.variableOrConstant)) { - var variable = LIRValueUtil.asVariable(speculativeMove.variableOrConstant); + if (!speculativeMove.location.isVariable() && speculativeMove.variableOrConstant.isVariable()) { + var variable = speculativeMove.variableOrConstant.asVariable(); if (definedVariables.containsKey(variable)) { continue; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 6f9384d97f97..9caf43cc32a0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -9,10 +9,11 @@ import jdk.vm.ci.meta.Value; public class ValueAllocationState extends AllocationState implements Cloneable { - protected Value value; + protected RAValue value; protected RAVInstruction.Base source; - public ValueAllocationState(Value value, RAVInstruction.Base source) { + public ValueAllocationState(RAValue raValue, RAVInstruction.Base source) { + var value = raValue.getValue(); if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { // StackSlot, RegisterValue is present in start block in label as predefined argument // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS @@ -20,7 +21,7 @@ public ValueAllocationState(Value value, RAVInstruction.Base source) { // We use variables as symbols for register validation // but real registers can also be used as that, in some cases. - this.value = value; + this.value = raValue; this.source = source; } else { throw GraalError.shouldNotReachHere("Invalid type of value used " + value); @@ -28,10 +29,14 @@ public ValueAllocationState(Value value, RAVInstruction.Base source) { } public ValueAllocationState(ValueAllocationState other) { - this.value = other.getValue(); + this.value = other.getRAValue(); } public Value getValue() { + return value.getValue(); + } + + public RAValue getRAValue() { return value; } @@ -49,7 +54,7 @@ public AllocationState meet(AllocationState other) { } var otherValueAllocState = (ValueAllocationState) other; - if (!this.value.equals(otherValueAllocState.getValue())) { + if (!this.value.equals(otherValueAllocState.getRAValue())) { return new ConflictedAllocationState(this, otherValueAllocState); } @@ -58,7 +63,7 @@ public AllocationState meet(AllocationState other) { @Override public boolean equals(AllocationState other) { - return other instanceof ValueAllocationState otherVal && this.value.equals(otherVal.getValue()); + return other instanceof ValueAllocationState otherVal && this.value.equals(otherVal.getRAValue()); } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index f3bfe50855ca..4995be663923 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -8,11 +8,11 @@ public class ValueNotInRegisterException extends RAVException { public LIRInstruction instruction; public BasicBlock block; - public Value variable; // Can be a constant or other symbolic value - public Value location; // Can be StackSlot, RegisterValue or memory + public RAValue variable; // Can be a constant or other symbolic value + public RAValue location; // Can be StackSlot, RegisterValue or memory public AllocationState state; - public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { + public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock block, RAValue variable, RAValue location, AllocationState state) { super(ValueNotInRegisterException.getErrorMessage(instruction, block, variable, location, state)); this.instruction = instruction; @@ -22,7 +22,7 @@ public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock blo this.state = state; } - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, Value variable, Value location, AllocationState state) { + static String getErrorMessage(LIRInstruction instruction, BasicBlock block, RAValue variable, RAValue location, AllocationState state) { var messageBuilder = new StringBuilder(); // @formatter:off messageBuilder @@ -42,7 +42,7 @@ static String getErrorMessage(LIRInstruction instruction, BasicBlock block, V messageBuilder.append("\n"); for (var conflictedState : confStates) { - messageBuilder.append(" - ").append(conflictedState.getValue()).append(" from ").append(conflictedState.source).append("\n"); + messageBuilder.append(" - ").append(conflictedState.getRAValue()).append(" from ").append(conflictedState.source).append("\n"); } } else { messageBuilder.append(state); From 786849a02615a49ff192aa41040b297c0a0d6c28 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sun, 15 Feb 2026 22:08:11 +0100 Subject: [PATCH 047/112] Resolve todos --- .../AliveConstraintViolationException.java | 1 - .../verifier/CircularDefinitionError.java | 2 - .../lir/alloc/verifier/ConflictResolver.java | 2 - ...nstantMaterializationConflictResolver.java | 41 ++++++++++--------- .../lir/alloc/verifier/FromJumpResolver.java | 1 - .../verifier/FromPredecessorsResolver.java | 9 +--- .../lir/alloc/verifier/FromUsageResolver.java | 9 ++-- .../verifier/KindsMismatchException.java | 1 - .../verifier/MergedBlockVerifierState.java | 11 ++--- .../alloc/verifier/MissingLocationError.java | 1 - .../verifier/PreRegisterAllocationPhase.java | 4 +- .../lir/alloc/verifier/RAVInstruction.java | 36 ++++++++-------- .../RegisterAllocationVerifierPhase.java | 6 +-- .../RegisterAllocationVerifierPhaseState.java | 5 ++- .../TargetLocationOverwrittenException.java | 8 ++-- .../verifier/ValueNotInRegisterException.java | 1 - 16 files changed, 58 insertions(+), 80 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index 91b18aecbfba..b6301ad7cf1f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -2,7 +2,6 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; -import jdk.vm.ci.meta.Value; @SuppressWarnings("serial") public class AliveConstraintViolationException extends RAVException { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java index 7382367c2f65..c408a9ed4cc1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java @@ -1,8 +1,6 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.Variable; import java.util.List; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index b8d83424871c..ccf279a57589 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -2,8 +2,6 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.Variable; -import jdk.vm.ci.meta.Value; import java.util.List; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 26c86521e85f..ca4ddf9ba666 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -4,7 +4,6 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.util.EconomicHashMap; @@ -40,11 +39,6 @@ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock bloc if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); - // TODO: Handle moves for canRematerializeToStack - // MOVE vstack:1, const // v1 - // MOVE rax, vstack:1 - // USE v1 - if (!op.dests.orig[0].isVariable()) { return; } @@ -64,10 +58,10 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted var confStates = conflictedState.getConflictedStates(); RAVariable variable = null; - RAValue constantValue = null; + ValueAllocationState constantState = null; - for (var states : confStates) { - var value = states.getRAValue(); + for (var state : confStates) { + var value = state.getRAValue(); if (value.isVariable()) { if (variable != null && !variable.equals(value)) { return null; @@ -75,15 +69,15 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted variable = value.asVariable(); } else if (value.getValue() instanceof ConstantValue) { - if (constantValue != null && !constantValue.equals(value)) { + if (constantState != null && !constantState.getRAValue().equals(value)) { return null; } - constantValue = value; + constantState = state; } } - if (!target.equals(variable) || constantValue == null) { + if (!target.equals(variable) || constantState == null) { return null; } @@ -91,11 +85,11 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted return null; } - if (!this.constantVariableMap.get(variable).equals(constantValue)) { + if (!this.constantVariableMap.get(variable).equals(constantState.getValue())) { return null; } - if (isRematerializedToWrongLocation(variable, location)) { + if (isRematerializedToWrongLocation(variable, constantState)) { throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); } @@ -113,7 +107,7 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati return null; } - if (isRematerializedToWrongLocation(variable, location)) { + if (isRematerializedToWrongLocation(variable, valueState)) { throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); } @@ -123,11 +117,18 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati return null; } - protected boolean isRematerializedToWrongLocation(RAVariable variable, RAValue raLocation) { - var location = raLocation.getValue(); - if (location instanceof StackSlot || location instanceof VirtualStackSlot) { - return !canRematerializeToStack.contains(variable); + protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllocationState state) { + if (canRematerializeToStack.contains(variable)) { + return false; + } + + // Cannot be rematerialized to stack + var source = state.getSource(); + if (source instanceof RAVInstruction.ValueMove move) { + var location = move.location.getValue(); + return location instanceof StackSlot || location instanceof VirtualStackSlot; + } else { + throw new IllegalStateException(); } - return false; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java index 87a15da533fb..5bb5f2ab50af 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java @@ -3,7 +3,6 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; -import jdk.vm.ci.meta.Value; import java.util.List; import java.util.Set; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index 581eea4d069d..6f182a09e4e6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -3,16 +3,11 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.meta.Value; import java.util.ArrayList; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -154,11 +149,11 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { RAValue toLocation; switch (instruction) { - case RAVInstruction.VirtualMove virtMove -> { + case RAVInstruction.ValueMove virtMove -> { toLocation = virtMove.location; fromLocation = null; } - case RAVInstruction.Move move -> { + case RAVInstruction.LocationMove move -> { fromLocation = move.from; toLocation = move.to; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java index b4e001de8023..b03191943b21 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -4,12 +4,9 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.meta.Value; import java.util.LinkedList; import java.util.List; @@ -104,12 +101,12 @@ private RAValue getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAV // register move. If we are wrong about the target register then, it will // get thrown out in the verification stage. switch (instruction) { - case RAVInstruction.VirtualMove move -> { + case RAVInstruction.ValueMove move -> { if (move.location.equals(location) && !move.variableOrConstant.equals(variable)) { throw new TargetLocationOverwrittenException(move, block); } } - case RAVInstruction.Move move -> { + case RAVInstruction.LocationMove move -> { if (move.to.equals(location)) { location = move.from; } @@ -238,7 +235,7 @@ public void resolvePhiFromUsage() { for (var instruction : instructions) { switch (instruction) { - case RAVInstruction.VirtualMove move -> { + case RAVInstruction.ValueMove move -> { this.tryMappingVariable(labelToBlockMap, move.variableOrConstant, move.location, entry, instruction); } case RAVInstruction.Op op -> { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java index b5313d3fb77e..cec30072a243 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -2,7 +2,6 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; -import jdk.vm.ci.meta.Value; @SuppressWarnings("serial") public class KindsMismatchException extends RAVException { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index cc47c061065c..c1a7d348dd14 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -6,11 +6,9 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.CastValue; import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; public class MergedBlockVerifierState { @@ -185,7 +183,7 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock } if (value.isIllegal()) { - continue; // TODO: remove IllegalValues from these arrays. + continue; } for (int j = 0; j < instruction.temp.count; j++) { @@ -257,8 +255,8 @@ public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstr public void update(RAVInstruction.Base instruction, BasicBlock block) { switch (instruction) { case RAVInstruction.Op op -> this.updateWithOp(op, block); - case RAVInstruction.VirtualMove virtMove -> this.updateWithVirtualMove(virtMove); - case RAVInstruction.Move move -> this.values.putClone(move.to, this.values.get(move.from)); + case RAVInstruction.ValueMove virtMove -> this.updateWithVirtualMove(virtMove); + case RAVInstruction.LocationMove move -> this.values.putClone(move.to, this.values.get(move.from)); default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } } @@ -303,13 +301,12 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { } } - protected void updateWithVirtualMove(RAVInstruction.VirtualMove virtMove) { + protected void updateWithVirtualMove(RAVInstruction.ValueMove virtMove) { if (virtMove.location.getValue() instanceof RegisterValue) { this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); } else if (virtMove.location.isVariable()) { // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD // Move before allocation - // TODO: maybe handle this better than VirtualMove with location as Variable // TestCase: BoxingTest.boxBoolean var locations = this.values.getValueLocations(virtMove.variableOrConstant); for (var location : locations) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java index cfe0b04112eb..7a9fe5b55ab0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java @@ -2,7 +2,6 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; -import jdk.vm.ci.meta.Value; @SuppressWarnings("serial") public class MissingLocationError extends RAVError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 47f2d083c089..5553fafca35e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -130,7 +130,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo var location = valueMov.getInput(); var variable = LIRValueUtil.asVariable(valueMov.getResult()); - var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, location); + var virtualMove = new RAVInstruction.ValueMove(instruction, variable, location); previousInstr.addVirtualMove(virtualMove); continue; // No need to store virtual move here, it is stored into previous instruction. } @@ -148,7 +148,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo var variable = LIRValueUtil.asVariable(valueMov.getInput()); var register = valueMov.getResult(); - var virtualMove = new RAVInstruction.VirtualMove(instruction, variable, register); + var virtualMove = new RAVInstruction.ValueMove(instruction, variable, register); previousInstr.addSpeculativeMove(virtualMove); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index c353ebb33e47..32ca489b7748 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -15,12 +15,10 @@ public class RAVInstruction { public static class Base { protected LIRInstruction lirInstruction; - protected List virtualMoveList; - protected List speculativeMoveList; + protected List virtualMoveList; + protected List speculativeMoveList; public Base(LIRInstruction lirInstruction) { - // We do not actually need the original instruction here, - // but is useful to have when debugging. this.lirInstruction = lirInstruction; this.virtualMoveList = new LinkedList<>(); this.speculativeMoveList = new LinkedList<>(); @@ -30,19 +28,19 @@ public LIRInstruction getLIRInstruction() { return lirInstruction; } - public void addVirtualMove(VirtualMove virtualMove) { + public void addVirtualMove(ValueMove virtualMove) { this.virtualMoveList.add(virtualMove); } - public List getVirtualMoveList() { + public List getVirtualMoveList() { return virtualMoveList; } - public void addSpeculativeMove(VirtualMove virtualMove) { - this.speculativeMoveList.add(virtualMove); + public void addSpeculativeMove(ValueMove speculativeMove) { + this.speculativeMoveList.add(speculativeMove); } - public List getSpeculativeMoveList() { + public List getSpeculativeMoveList() { return speculativeMoveList; } } @@ -204,11 +202,11 @@ public String toString() { } } - public static class Move extends Base { + public static class LocationMove extends Base { public RAValue from; public RAValue to; - public Move(LIRInstruction instr, Value from, Value to) { + public LocationMove(LIRInstruction instr, Value from, Value to) { super(instr); this.from = RAValue.create(from); this.to = RAValue.create(to); @@ -219,7 +217,7 @@ public String toString() { } } - public static class RegMove extends Move { + public static class RegMove extends LocationMove { // public RegisterValue from; // public RegisterValue to; public RAValue from; @@ -238,7 +236,7 @@ public String toString() { // StackMove class to handle STACKMOVE instruction, temporary for now // before I decide how all Moves should be handled + all possible combinations. - public static class StackMove extends Move { + public static class StackMove extends LocationMove { public RAValue from; public RAValue to; @@ -257,7 +255,7 @@ public String toString() { } } - public static class Reload extends Move { + public static class Reload extends LocationMove { public RAValue from; public RAValue to; @@ -272,7 +270,7 @@ public String toString() { } } - public static class Spill extends Move { + public static class Spill extends LocationMove { public RAValue from; public RAValue to; @@ -287,12 +285,12 @@ public String toString() { } } - public static class VirtualMove extends Move { + public static class ValueMove extends Base { public RAValue variableOrConstant; - public RAValue location; + public RAValue location; // Can also be another variable! - public VirtualMove(LIRInstruction instr, Value variableOrConstant, Value location) { - super(instr, variableOrConstant, location); + public ValueMove(LIRInstruction instr, Value variableOrConstant, Value location) { + super(instr); this.variableOrConstant = RAValue.create(variableOrConstant); this.location = RAValue.create(location); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index f8b5dc81130e..845d72bf787b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -5,9 +5,7 @@ import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRInstruction; -import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.AllocationPhase; @@ -187,8 +185,8 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot // This isn't really a virtual move, but it currently acts the same, so we keep it, - // we take constants as variables. TODO: maybe remove virtual move altogether for Move(reg, var/constant) - return new RAVInstruction.VirtualMove(instruction, new ConstantValue(result.getValueKind(), constant), result); + // we take constants as variables. + return new RAVInstruction.ValueMove(instruction, new ConstantValue(result.getValueKind(), constant), result); } return null; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java index 4431d658f431..8afdc0547c36 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -31,8 +31,9 @@ public boolean shouldBeVerified(LIRGenerationResult lirGenRes) { } public Map createInstructionMap(LIRGenerationResult lirGenRes) { - this.verifierInstructions.put(lirGenRes, new IdentityHashMap<>()); - return this.verifierInstructions.get(lirGenRes); + var idMap = new IdentityHashMap(); + this.verifierInstructions.put(lirGenRes, idMap); + return idMap; } public Map getInstructionMap(LIRGenerationResult lirGenRes) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java index 0966b7623ce4..747ae55554e9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java @@ -4,16 +4,16 @@ @SuppressWarnings("serial") public class TargetLocationOverwrittenException extends RAVException { - public RAVInstruction.VirtualMove virtualMove; + public RAVInstruction.ValueMove valueMove; public BasicBlock block; - public TargetLocationOverwrittenException(RAVInstruction.VirtualMove move, BasicBlock block) { + public TargetLocationOverwrittenException(RAVInstruction.ValueMove move, BasicBlock block) { super(getErrorMessage(move, block)); - this.virtualMove = move; + this.valueMove = move; this.block = block; } - static String getErrorMessage(RAVInstruction.VirtualMove move, BasicBlock block) { + static String getErrorMessage(RAVInstruction.ValueMove move, BasicBlock block) { return "Target location " + move.location + " was overwritten by " + move.lirInstruction + " in " + block; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 4995be663923..c48ec7c84f32 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -2,7 +2,6 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; -import jdk.vm.ci.meta.Value; @SuppressWarnings("serial") public class ValueNotInRegisterException extends RAVException { From 6f2564f004d9a6a6d00caadbc5c4a23579447582 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 17 Feb 2026 23:12:45 +0100 Subject: [PATCH 048/112] Make Unknown state create conflicts --- .../verifier/ConflictedAllocationState.java | 6 ++++- ...nstantMaterializationConflictResolver.java | 10 ++++---- .../verifier/FromPredecessorsResolver.java | 4 ++-- .../alloc/verifier/LabelConflictResolver.java | 8 +++++-- .../verifier/MergedBlockVerifierState.java | 21 ++++++++++------- .../verifier/RegisterAllocationVerifier.java | 16 +++++-------- .../verifier/UnknownAllocationState.java | 12 +++++++++- .../alloc/verifier/ValueAllocationState.java | 23 ++++++++++++++++--- 8 files changed, 68 insertions(+), 32 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index f1fbf3332faa..31e67ff84c26 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -18,7 +18,7 @@ public ConflictedAllocationState(ValueAllocationState state1, ValueAllocationSta this.conflictedStates.add(state2); } - private ConflictedAllocationState(Set conflictedStates) { + protected ConflictedAllocationState(Set conflictedStates) { this.conflictedStates = new EconomicHashSet<>(conflictedStates); } @@ -46,6 +46,10 @@ public AllocationState meet(AllocationState other) { newlyConflictedState.conflictedStates.addAll(conflictedState.conflictedStates); } + if (other instanceof UnknownAllocationState) { + newlyConflictedState.conflictedStates.add(ValueAllocationState.createIllegal()); + } + return newlyConflictedState; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index ca4ddf9ba666..987a61e8f4d1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -60,8 +60,8 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted RAVariable variable = null; ValueAllocationState constantState = null; - for (var state : confStates) { - var value = state.getRAValue(); + for (var valAllocState : confStates) { + var value = valAllocState.getRAValue(); if (value.isVariable()) { if (variable != null && !variable.equals(value)) { return null; @@ -73,7 +73,7 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted return null; } - constantState = state; + constantState = valAllocState; } } @@ -93,7 +93,7 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); } - return new ValueAllocationState(variable, null); + return new ValueAllocationState(variable, constantState.getSource(), constantState.block); } @Override @@ -111,7 +111,7 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); } - return new ValueAllocationState(variable, valueState.source); + return new ValueAllocationState(variable, valueState.getSource(), valueState.getBlock()); } return null; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index 6f182a09e4e6..053f44aae8bc 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -203,7 +203,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { variablesToBePropagated.add(variable); for (var location : locations) { if (state != null) { - state.values.put(location, new ValueAllocationState(variable, labelInstr)); + state.values.put(location, new ValueAllocationState(variable, labelInstr, defBlock)); } } } @@ -254,7 +254,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var variable = itToBePropagated.next(); var locations = variableToLocations.get(variable); for (var location : locations) { - succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr)); + succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr, defBlock)); } newLoc.put(variable, new EconomicHashSet<>(locations)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java index 2fafdca68aec..cba80e2cc03f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java @@ -1,5 +1,6 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.util.EconomicHashMap; @@ -14,11 +15,13 @@ public class LabelConflictResolver implements ConflictResolver { protected LIR lir; protected BlockMap> blockInstructions; + protected Map> blockMap; protected Map labelMap; protected Map> rules; protected Map> expandedRules; public LabelConflictResolver() { + this.blockMap = new EconomicHashMap<>(); this.labelMap = new EconomicHashMap<>(); this.rules = new EconomicHashMap<>(); this.expandedRules = new EconomicHashMap<>(); @@ -47,6 +50,7 @@ protected void buildLabelMap() { var labelVariable = labelInstr.dests.orig[i]; labelMap.put(labelVariable.asVariable(), labelInstr); + blockMap.put(labelVariable.asVariable(), block); } } } @@ -129,7 +133,7 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted return null; } - return new ValueAllocationState(target, labelMap.get(target)); + return new ValueAllocationState(target, labelMap.get(target), blockMap.get(target)); } @Override @@ -143,6 +147,6 @@ public ValueAllocationState resolveValueState(RAVariable target, ValueAllocation return null; } - return new ValueAllocationState(target, labelMap.get(target)); + return new ValueAllocationState(target, labelMap.get(target), blockMap.get(target)); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index c1a7d348dd14..30203015575c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -84,7 +84,12 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } if (orig.equals(curr)) { - // In this case nothing has changed so we have nothing to verify + // For these cases we do not consider checking state taking the original + // register as a symbol, because there's too many cases when this does + // not work, for example RETURN with rax tends to contain the actual + // generated variable instead of rax symbol, or NEAR_FOREIGN_CALL + // keeps its own registers before and after allocation, but those + // can also contain different variable symbols. continue; } @@ -255,7 +260,7 @@ public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstr public void update(RAVInstruction.Base instruction, BasicBlock block) { switch (instruction) { case RAVInstruction.Op op -> this.updateWithOp(op, block); - case RAVInstruction.ValueMove virtMove -> this.updateWithVirtualMove(virtMove); + case RAVInstruction.ValueMove virtMove -> this.updateWithVirtualMove(virtMove, block); case RAVInstruction.LocationMove move -> this.values.putClone(move.to, this.values.get(move.from)); default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } @@ -283,9 +288,9 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { if (location.equals(variable)) { // Only check register validity if it was changed by the register allocator // for example: rbp is used as input to start block and forbidden to be used by the allocator - this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op)); + this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op, block)); } else { - this.values.put(location, new ValueAllocationState(variable, op)); + this.values.put(location, new ValueAllocationState(variable, op, block)); } } @@ -301,19 +306,19 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { } } - protected void updateWithVirtualMove(RAVInstruction.ValueMove virtMove) { + protected void updateWithVirtualMove(RAVInstruction.ValueMove virtMove, BasicBlock block) { if (virtMove.location.getValue() instanceof RegisterValue) { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove, block)); } else if (virtMove.location.isVariable()) { // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD // Move before allocation // TestCase: BoxingTest.boxBoolean var locations = this.values.getValueLocations(virtMove.variableOrConstant); for (var location : locations) { - this.values.put(location, new ValueAllocationState(virtMove.location, virtMove)); + this.values.put(location, new ValueAllocationState(virtMove.location, virtMove, block)); } } else { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove)); + this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove, block)); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index aaf88cc9d1cb..a79223c06de6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -120,20 +120,16 @@ public void calculateEntryBlocks() { for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); - MergedBlockVerifierState succState; if (this.blockEntryStates.get(succ) == null) { - succState = new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantMaterializationConflictResolver, labelConflictResolver); + var succState = new MergedBlockVerifierState(state, registerAllocationConfig, phiResolution, constantMaterializationConflictResolver, labelConflictResolver); - // Either there's no state because it was not yet processed first part of the condition - // or, we need to reset it because label changed, second part of the condition - - // Label change will hopefully work for children of children and there won't be need for us - // to reset all the children. - this.blockStates.put(succ, null); - } else { - succState = this.blockEntryStates.get(succ); + this.blockEntryStates.put(succ, succState); + worklist.remove(succ); + worklist.add(succ); + continue; } + var succState = this.blockEntryStates.get(succ); var succLabelOp = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); if (succState.meetWith(state) || this.hasMissingRegistersInLabel(succLabelOp)) { // if we resolved a phi, then we also need to process children again, diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index f7bcea2e677f..2302cf3bda1a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -10,7 +10,17 @@ public boolean isUnknown() { @Override public AllocationState meet(AllocationState other) { - return other; + if (other.isUnknown()) { + return this; + } + + if (other instanceof ConflictedAllocationState conflictedState) { + var newConfState = new ConflictedAllocationState(conflictedState.conflictedStates); + newConfState.addConflictedValue(ValueAllocationState.createIllegal()); + return newConfState; + } + + return new ConflictedAllocationState((ValueAllocationState) other, ValueAllocationState.createIllegal()); } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 9caf43cc32a0..b040b7445051 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -1,5 +1,6 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRValueUtil; @@ -11,8 +12,9 @@ public class ValueAllocationState extends AllocationState implements Cloneable { protected RAValue value; protected RAVInstruction.Base source; + protected BasicBlock block; - public ValueAllocationState(RAValue raValue, RAVInstruction.Base source) { + public ValueAllocationState(RAValue raValue, RAVInstruction.Base source, BasicBlock block) { var value = raValue.getValue(); if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { // StackSlot, RegisterValue is present in start block in label as predefined argument @@ -23,6 +25,7 @@ public ValueAllocationState(RAValue raValue, RAVInstruction.Base source) { // but real registers can also be used as that, in some cases. this.value = raValue; this.source = source; + this.block = block; } else { throw GraalError.shouldNotReachHere("Invalid type of value used " + value); } @@ -30,6 +33,13 @@ public ValueAllocationState(RAValue raValue, RAVInstruction.Base source) { public ValueAllocationState(ValueAllocationState other) { this.value = other.getRAValue(); + this.source = other.getSource(); + this.block = other.getBlock(); + } + + public static ValueAllocationState createIllegal() { + // TODO: pass in the block that created this value + return new ValueAllocationState(new RAValue(Value.ILLEGAL), null, null); } public Value getValue() { @@ -44,13 +54,20 @@ public RAVInstruction.Base getSource() { return source; } + public BasicBlock getBlock() { + return block; + } + public AllocationState meet(AllocationState other) { if (other.isUnknown()) { - return this; + return new ConflictedAllocationState(createIllegal(), this); } if (other.isConflicted()) { - return other; + var oldConfState = (ConflictedAllocationState) other; + var newConfState = new ConflictedAllocationState(oldConfState.conflictedStates); + newConfState.addConflictedValue(this); + return newConfState; } var otherValueAllocState = (ValueAllocationState) other; From 05a32e618f8dcf8ae2aeaf9a17d2e94518468b99 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 17 Feb 2026 23:19:33 +0100 Subject: [PATCH 049/112] Save register in exception --- .../lir/alloc/verifier/InvalidRegisterUsedException.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java index 8f4f8fe8c300..866664945094 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java @@ -4,9 +4,11 @@ @SuppressWarnings("serial") public class InvalidRegisterUsedException extends RAVException { + public Register register; public InvalidRegisterUsedException(Register register) { super(getErrorMessage(register)); + this.register = register; } static String getErrorMessage(Register register) { From a598465a0eaa4b3ec494bde7d13e8a7549cd6066 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 18 Feb 2026 11:41:15 +0100 Subject: [PATCH 050/112] Add comments to verifer classes --- .../AliveConstraintViolationException.java | 13 ++ .../lir/alloc/verifier/AllocationState.java | 33 +++++ .../verifier/CircularDefinitionError.java | 19 +++ .../lir/alloc/verifier/ConflictResolver.java | 32 +++++ .../verifier/ConflictedAllocationState.java | 32 +++++ ...nstantMaterializationConflictResolver.java | 47 +++++++ .../lir/alloc/verifier/FromJumpResolver.java | 24 +++- .../verifier/FromPredecessorsResolver.java | 35 +++-- .../lir/alloc/verifier/FromUsageResolver.java | 5 + .../verifier/FromUsageResolverGlobal.java | 3 - .../InvalidRegisterUsedException.java | 4 + .../verifier/KindsMismatchException.java | 14 +- .../alloc/verifier/LabelNotResolvedError.java | 5 + .../verifier/MergedAllocationStateMap.java | 81 ++++++++++-- .../verifier/MergedBlockVerifierState.java | 124 +++++++++++++++--- .../alloc/verifier/MissingLocationError.java | 10 ++ .../lir/alloc/verifier/PhiResolution.java | 38 +++++- .../verifier/PreRegisterAllocationPhase.java | 37 +++++- .../compiler/lir/alloc/verifier/RAVError.java | 10 +- .../lir/alloc/verifier/RAVException.java | 9 +- .../RAVFailedVerificationException.java | 5 + .../lir/alloc/verifier/RAVInstruction.java | 106 ++++++++++++++- .../compiler/lir/alloc/verifier/RAValue.java | 23 ++++ .../lir/alloc/verifier/RAVariable.java | 8 ++ .../verifier/RegisterAllocationVerifier.java | 69 +++++++++- .../RegisterAllocationVerifierPhase.java | 22 +++- .../RegisterAllocationVerifierPhaseState.java | 32 +++++ .../alloc/verifier/TaggedConstantFactory.java | 3 + .../verifier/TaggedDataPointerConstant.java | 4 + .../alloc/verifier/TaggedJavaConstant.java | 4 + .../TargetLocationOverwrittenException.java | 4 + .../verifier/UnknownAllocationState.java | 14 ++ .../verifier/UnknownInstructionError.java | 5 + .../alloc/verifier/ValueAllocationState.java | 26 +++- .../verifier/ValueNotInRegisterException.java | 12 ++ .../lir/alloc/verifier/VerifierPrinter.java | 8 ++ 36 files changed, 851 insertions(+), 69 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index b6301ad7cf1f..0161a7f2d8e1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -3,11 +3,24 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; +/** + * Violation of the alive arguments occurred, + * same location was marked as alive argument + * as well as input or output. + */ @SuppressWarnings("serial") public class AliveConstraintViolationException extends RAVException { public LIRInstruction instruction; public BasicBlock block; + /** + * Construct an AliveConstraintViolationException + * + * @param instruction Instruction where violation occurred + * @param block Block where violation occurred + * @param location Location that is being shared + * @param asDest Alive location was used as an output + */ public AliveConstraintViolationException(LIRInstruction instruction, BasicBlock block, RAValue location, boolean asDest) { super(AliveConstraintViolationException.getErrorMessage(instruction, block, location, asDest)); this.instruction = instruction; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index f0521ff2ad56..3011bcdab5ce 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -1,20 +1,53 @@ package jdk.graal.compiler.lir.alloc.verifier; +/** + * Interface for AllocationState stored in AllocationStateMap, + * describing what state physical location is in. + */ public abstract class AllocationState { + /** + * No location is ever just null, always at least Unknown. + * + * @return + */ public static AllocationState getDefault() { return UnknownAllocationState.INSTANCE; } + /** + * Shortcut to check if state is Unknown. + * + * @return Is unknown state + */ public boolean isUnknown() { return false; } + /** + * Shortcut to check if state is ConflictedState. + * + * @return Is ConflictedState + */ public boolean isConflicted() { return false; } + /** + * Create a copy of this state, necessary + * for state copies made over program graph edges. + * + * @return Newly copied state + */ public abstract AllocationState clone(); + /** + * Meet a state from different block coming from edge in + * the program graph, decide what result of said two states + * should be. + * + * @param other Other state coming from a predecessor edge + * @return What is the new state the location is in. + */ public abstract AllocationState meet(AllocationState other); public abstract boolean equals(AllocationState other); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java index c408a9ed4cc1..41fe01d5596a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java @@ -4,8 +4,27 @@ import java.util.List; +/** + * Circular definition was created between + * a label block and one of its predecessors, + * meaning a variable defined in the first block + * is being propagated back to it by one of its + * predecessors, which is an invalid state. + *

+ * Defining block's entry state would contain + * the variable it is defining in its label. + *

+ */ @SuppressWarnings("serial") public class CircularDefinitionError extends RAVError { + /** + * Construct a CircularDefinitionError + * + * @param defBlock Block where label variables are first defined + * @param predecessor The predecessor block creating the circular definition + * @param label Label defining variables + * @param beingPropagated Which variables are being propagated to defBlock from the predecessor + */ public CircularDefinitionError(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { super(getErrorMessage(defBlock, predecessor, label, beingPropagated)); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index ccf279a57589..0ed5a8c5d23b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -5,10 +5,42 @@ import java.util.List; +/** + * Resolve ConflictedAllocationState occurrences based + * on internal set of rules. + *

+ * In-case comparison of ValueAllocationState fails, it + * also might get resolved by this. + *

+ */ public interface ConflictResolver { + /** + * ConflictResolver can prepare its own internal state here + * so it can later resolve conflicts. + * + * @param lir LIR + * @param blockInstructions IR of the Verifier + */ void prepare(LIR lir, BlockMap> blockInstructions); + /** + * Resolve an issue stemming from ValueAllocationState not having the correct value + * in verification phase. + * + * @param target Variable we are looking to resolve to + * @param valueState Current ValueAllocationState instance + * @param location Location where the valueState is stored + * @return ValueAllocationState instance if conflict is resolved, otherwise null. + */ ValueAllocationState resolveValueState(RAVariable target, ValueAllocationState valueState, RAValue location); + /** + * Resolve a ConflictedAllocationState to ValueAllocationState based on the target variable. + * + * @param target Variable we are looking to resolve to + * @param conflictedState Set of conflicted states + * @param location Location where the valueState is stored + * @return ValueAllocationState instance if conflict is resolved, otherwise null. + */ ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 31e67ff84c26..a0c9a6583494 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -5,6 +5,12 @@ import java.util.Arrays; import java.util.Set; +/** + * Conflicted allocation state - two or more ValueAllocationState instances + * have collided and either of them can be stored at said location, needs + * to be resolved by either overwriting the location with a new ValueAllocationState instance + * or by a ConflictResolver implementation. + */ public class ConflictedAllocationState extends AllocationState { protected Set conflictedStates; @@ -26,6 +32,11 @@ public void addConflictedValue(ValueAllocationState state) { this.conflictedStates.add(state); } + /** + * Get the set of all ValueAllocationState instances conflicting in this state. + * + * @return Set of ValueAllocationState instances + */ public Set getConflictedStates() { return this.conflictedStates; } @@ -35,6 +46,13 @@ public boolean isConflicted() { return true; } + /** + * Any state coming here will be added to the conflict set + * and create a new ConflictedAllocationState instance. + * + * @param other Other state coming from a predecessor edge + * @return ConflictedAllocationState with predecessor state added up + */ @Override public AllocationState meet(AllocationState other) { var newlyConflictedState = new ConflictedAllocationState(this.getConflictedStates()); @@ -47,6 +65,11 @@ public AllocationState meet(AllocationState other) { } if (other instanceof UnknownAllocationState) { + // Unknown state creates an Illegal ValueAllocationState inside it, because + // the unknown state is coming from a different predecessor to the same block, + // and it means that this location was not defined there, but it was defined in a + // different predecessor block, meaning it's now in a conflicted state, where + // it either is defined or it is not - should not be used in further blocks. newlyConflictedState.conflictedStates.add(ValueAllocationState.createIllegal()); } @@ -58,6 +81,15 @@ public AllocationState clone() { return new ConflictedAllocationState(this.conflictedStates); } + /** + * We do not compare conflicted state on its contents, + * whenever new one would be created as a result, the + * set of contents would remain the same, if values + * are equal based on RAValue rules. + * + * @param other Other state we are comparing it to + * @return Are both states conflicted? + */ @Override public boolean equals(AllocationState other) { return other.isConflicted(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 987a61e8f4d1..610c7f4bf0d9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -14,6 +14,12 @@ import java.util.Map; import java.util.Set; +/** + * Resolve conflicts made by the constant materialization process. + *

+ * Also checks if the constant can materialize to stack. + *

+ */ public class ConstantMaterializationConflictResolver implements ConflictResolver { protected Map constantVariableMap; protected Set canRematerializeToStack; @@ -23,6 +29,12 @@ public ConstantMaterializationConflictResolver() { this.canRematerializeToStack = new EconomicHashSet<>(); } + /** + * Creates a variable to constant mapping. + * + * @param lir LIR + * @param blockInstructions IR of the Verifier + */ @Override public void prepare(LIR lir, BlockMap> blockInstructions) { for (var blockId : lir.getBlocks()) { @@ -35,6 +47,12 @@ public void prepare(LIR lir, BlockMap> blockInstructio } } + /** + * Add variable to constant mapping from instruction contents. + * + * @param instruction Instruction we are looking at for LoadConstantOp + * @param block Block where instruction is from + */ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); @@ -53,6 +71,15 @@ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock bloc } } + /** + * Resolve conflict of target variable and the constant it represents + * to the ValueAllocationState of the target variable. + * + * @param target Variable we are looking to resolve to + * @param conflictedState Set of conflicted states + * @param location Location where the valueState is stored + * @return target variable stored in ValueAllocationState or null. + */ @Override public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { var confStates = conflictedState.getConflictedStates(); @@ -96,6 +123,14 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted return new ValueAllocationState(variable, constantState.getSource(), constantState.block); } + /** + * Resolve ValueAllocationState of a constant to the target variable. + * + * @param variable Variable we are looking to resolve to + * @param valueState Current ValueAllocationState instance + * @param location Location where the valueState is stored + * @return target variable stored in ValueAllocationState or null. + */ @Override public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocationState valueState, RAValue location) { if (!this.constantVariableMap.containsKey(variable)) { @@ -117,6 +152,18 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati return null; } + /** + * Check if variable can be rematerialized to said location based on the + * original instruction source, stored in ValueAllocationState. + * + *

+ * Check if variable cannot rematerialize to stack and if it did so. + *

+ * + * @param variable Target variable + * @param state State it is in + * @return Was it rematerialized to wrong location? + */ protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllocationState state) { if (canRematerializeToStack.contains(variable)) { return false; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java index 5bb5f2ab50af..508f6988f818 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java @@ -7,6 +7,12 @@ import java.util.List; import java.util.Set; +/** + * Resolve label variable location before we jump to + * said block from its predecessors by looking at + * their state and finding said variables in their + * JumpOp. + */ public class FromJumpResolver { public LIR lir; public BlockMap> blockInstructions; @@ -18,6 +24,15 @@ public FromJumpResolver(LIR lir, BlockMap> blockInstru this.blockStates = blockStates; } + /** + * Resolve label variables to said block from their state + * and their jump variables. + *

+ * If multiple locations are chosen, we take the last one + *

+ * + * @param block Target block + */ public void resolvePhi(BasicBlock block) { var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getFirst(); for (int i = 0; i < labelInstr.dests.count; i++) { @@ -51,6 +66,8 @@ public void resolvePhi(BasicBlock block) { return; } + // Selects the location that was used most recently, + // but it has to be used recently by all the predecessors. for (int j = 0; j < block.getPredecessorCount(); j++) { int time = -1; RAValue blockReg = null; @@ -79,14 +96,11 @@ public void resolvePhi(BasicBlock block) { location = locations.stream().findFirst().get(); } - var registerValue = location; - // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); - - labelInstr.dests.curr[i] = registerValue; + labelInstr.dests.curr[i] = location; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = registerValue; + jump.alive.curr[i] = location; } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java index 053f44aae8bc..9e5083c50130 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java @@ -14,6 +14,11 @@ import java.util.Queue; import java.util.Set; +/** + * Resolve label variable locations by cross-referencing + * state from its predecessors and settling on a single + * location for every variable. + */ public class FromPredecessorsResolver { public LIR lir; public BlockMap> blockInstructions; @@ -31,7 +36,7 @@ public FromPredecessorsResolver(LIR lir, BlockMap> blo * Fill in missing variable locations for current block's label instruction and predecessor * jump instructions. *

- * We are looking for intersection between locations of individual processors, this should + * We are looking for intersection between locations of individual predecessors, this should * give us a single register that is used for the phi, and is necessary for jump to verify * that contents are alive that that point and for label to define where the result of phi * will be held to used in said block. @@ -63,6 +68,8 @@ public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op return false; } + // Selects the location that was used most recently, + // but it has to be used recently by all the predecessors. for (int j = 0; j < block.getPredecessorCount(); j++) { int time = -1; RAValue blockReg = null; @@ -88,20 +95,26 @@ public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op location = locations.stream().findFirst().get(); } - var registerValue = location; - // var registerValue = location.asValue(labelInstr.dests.orig[i].getValueKind()); - - labelInstr.dests.curr[i] = registerValue; + labelInstr.dests.curr[i] = location; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = registerValue; + jump.alive.curr[i] = location; } } return true; } + /** + * Propagate newly defined label variable locations to all successors + * of said block, this needs to be because successor state still contain + * old locations and were already processed. + *

+ * TODO: incorporate new meet logic here + * + * @param defBlock Block defining new variable locations with its label. + */ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); @@ -111,7 +124,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var defVariableToLocations = new EconomicHashMap>(); var defBlockVariablesToPropagate = new ArrayList(); - for (int i = 0; i < labelInstr.dests.count; i++) { + for (int i = 0; i < labelInstr.dests.count; i++) { // Init locations to propagate by label variables var register = labelInstr.dests.curr[i]; var variable = labelInstr.dests.orig[i].asVariable(); @@ -168,7 +181,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { while (itToPropagate.hasNext()) { var variable = itToPropagate.next(); var locations = variableToLocations.get(variable); - locations.remove(location); + locations.remove(location); // Location overwritten, no need to distribute further. } } @@ -184,7 +197,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { var variable = itToPropagate.next(); var locations = variableToLocations.get(variable); if (fromLocation != null && locations.contains(fromLocation)) { - locations.add(toLocation); + locations.add(toLocation); // New location to propagate } else if (locations.contains(toLocation)) { locations.remove(toLocation); // Overwritten } @@ -202,7 +215,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { variablesToBePropagated.add(variable); for (var location : locations) { - if (state != null) { + if (state != null) { // State of successor blocks also needs to contain these, if it was not overwritten. state.values.put(location, new ValueAllocationState(variable, labelInstr, defBlock)); } } @@ -253,7 +266,7 @@ public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { while (itToBePropagated.hasNext()) { var variable = itToBePropagated.next(); var locations = variableToLocations.get(variable); - for (var location : locations) { + for (var location : locations) { // Update entry state for new predecessors! succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr, defBlock)); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java index b03191943b21..4316ae765a15 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java @@ -14,6 +14,11 @@ import java.util.Queue; import java.util.Set; +/** + * Resolve label variable locations by finding their first usage + * and going in-reverse back to the label to handle any moves + * in-between the label and usage, selecting a single location. + */ class FromUsageResolver { protected LIR lir; protected BlockMap> blockInstructions; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 5fbb938bfbd5..6c1637aa7968 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -3,12 +3,9 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.meta.Value; import java.util.ArrayList; import java.util.LinkedList; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java index 866664945094..8910cb05fd69 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java @@ -2,6 +2,10 @@ import jdk.vm.ci.code.Register; +/** + * Invalid register was used in allocation + * as defined by the RegisterAllocationConfig. + */ @SuppressWarnings("serial") public class InvalidRegisterUsedException extends RAVException { public Register register; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java index cec30072a243..cbbe15818913 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -3,6 +3,9 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; +/** + * Kinds are not matching between two values. + */ @SuppressWarnings("serial") public class KindsMismatchException extends RAVException { public LIRInstruction instruction; @@ -11,6 +14,15 @@ public class KindsMismatchException extends RAVException { public RAValue value2; public boolean origVsCurr; + /** + * Construct a KindsMismatchException + * + * @param instruction Instruction where violation occurred + * @param block Block where violation occurred + * @param value1 First value in comparison, original variable. + * @param value2 Second value in comparison, either current location or value stored in state + * @param origVsCurr Comparing original variable to current location + */ public KindsMismatchException(LIRInstruction instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { super(KindsMismatchException.getErrorMessage(instruction, block, value1, value2, origVsCurr)); @@ -23,7 +35,7 @@ public KindsMismatchException(LIRInstruction instruction, BasicBlock block, R static String getErrorMessage(LIRInstruction instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { if (origVsCurr) { - return value1.getValue().getValueKind() + " has different kind after allocation: " + value2.getValue().getValueKind() + " in " + instruction + " in block " + block; + return value1.getValue() + " has different kind after allocation: " + value2.getValue() + " in " + instruction + " in block " + block; } return "Value in location has different kind: " + value1.getValue().getValueKind() + " vs. " + value2.getValue().getValueKind() + " in " + instruction + " in block " + block; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java index d595fe00d194..575386975aa6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java @@ -2,6 +2,11 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; +/** + * Could not resolve label variable locations + * and thus cannot proceed further, an error + * caused by the Verifier, non-recoverable. + */ @SuppressWarnings("serial") public class LabelNotResolvedError extends RAVError { public BasicBlock block; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index be8fd0036ece..ca8cd417f571 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -8,18 +8,18 @@ import java.util.Map; import java.util.Set; +/** + * Mapping between a location and it's AllocationState. + */ public class MergedAllocationStateMap { /** - * These are instances of Value we need to keep for getValueLocations, - * indexed by their string representation. - *

- * Because Values have their own toString implementation, - * that the hash map uses and for some Values we do not - * want this (especially due to kinds), which are irrelevant - * for location indices, we have a getValueKeyString which - * does what we need. + * Internal map maintaining the mapping. */ protected Map internalMap; + /** + * Integer describing when was the value inserted, + * the bigger it is, the newer the value is. + */ protected Map locationTimings; /** * Prioritized locations are ones made by the register allocator itself. @@ -31,8 +31,15 @@ public class MergedAllocationStateMap { * If there's multiple, then time should make the difference. */ protected Map prioritizedLocations; + /** + * Internal constant for locationTimings. + */ protected int time; + /** + * Register allocation config describing which registers + * can be used. + */ protected RegisterAllocationConfig registerAllocationConfig; public MergedAllocationStateMap(RegisterAllocationConfig registerAllocationConfig) { @@ -70,11 +77,30 @@ public void putAsPrioritized(RAValue key, AllocationState value) { this.prioritizedLocations.put(key, true); } + /** + * Put a new state for location to the map, + * while checking if register can be allocated to. + * + * @param key Location used + * @param state State to store + */ public void put(RAValue key, AllocationState state) { this.checkRegisterDestinationValidity(key); putWithoutRegCheck(key, state); } + /** + * Put a new state for location to the map, + * without checking if the register can actually be used. + *

+ * This is useful for registers that are used by the abi + * in the first label, but can actually never be changed, + * like rbp. + *

+ * + * @param key Location used + * @param state State to store + */ public void putWithoutRegCheck(RAValue key, AllocationState state) { locationTimings.put(key, time++); internalMap.put(key, state); @@ -84,13 +110,19 @@ public void putWithoutRegCheck(RAValue key, AllocationState state) { } } - public void putClone(RAValue key, AllocationState value) { - if (value.isUnknown()) { - this.put(key, value); + /** + * Put a copied state to a location, used when merging. + * + * @param key Location used + * @param state State to store + */ + public void putClone(RAValue key, AllocationState state) { + if (state.isUnknown()) { + this.put(key, state); return; } - this.put(key, value.clone()); + this.put(key, state.clone()); } public void putCloneAsPrioritized(RAValue key, AllocationState value) { @@ -102,12 +134,24 @@ public boolean isPrioritized(RAValue key) { return prioritizedLocations.containsKey(key); } + /** + * Get time when the location was inserted. + * + * @param key Location used + * @return integer describing the insertion time - bigger = newer. + */ public int getKeyTime(RAValue key) { var time = locationTimings.get(key); assert time != null : "Time for key " + key + " not present."; return time; } + /** + * Get set of locations holding this particular variable/constant. + * + * @param value Symbol we are looking for + * @return Set of locations that hold said symbol + */ public Set getValueLocations(RAValue value) { Set locations = new EconomicHashSet<>(); for (var entry : this.internalMap.entrySet()) { @@ -120,6 +164,13 @@ public Set getValueLocations(RAValue value) { return locations; } + /** + * Merge two maps together, source is generally + * the predecessor to the current block (this state map). + * + * @param source Predecessor merging to here + * @return Was this map changed? + */ public boolean mergeWith(MergedAllocationStateMap source) { boolean changed = false; for (var entry : source.internalMap.entrySet()) { @@ -141,6 +192,12 @@ public boolean mergeWith(MergedAllocationStateMap source) { return changed; } + /** + * Check if register can be used by the register allocator. + * If not allowed, an exception is thrown. + * + * @param raLocation Value that could be a register. + */ protected void checkRegisterDestinationValidity(RAValue raLocation) { var location = raLocation.getValue(); if (!ValueUtil.isRegister(location)) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 30203015575c..bc512ab34820 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -11,12 +11,21 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ValueKind; +/** + * Verification state a block is in. + */ public class MergedBlockVerifierState { + /** + * Map maintaining mapping between locations and their state. + */ public MergedAllocationStateMap values; protected PhiResolution phiResolution; protected RegisterAllocationConfig registerAllocationConfig; + /** + * Conflict resolver for constant materialization. + */ protected ConflictResolver conflictConstantResolver; protected ConflictResolver labelConflictResolver; @@ -46,17 +55,25 @@ public MergedAllocationStateMap getValues() { return values; } - // TODO: reconsider the merging/meeting logic - // It might make sense to only keep certain states if it is certain to be defined / have a value present - // If a new value was defined in one branch in a certain register and other branches do not have anything in this - // register, then the value could not be defined at merging block and if used later it could be verified as valid - // but it is not. - // If overwritten in one branch but block before branching also has a certain value here - // it would just be marked as unknown / conflicted -> Only care if value after merge is ValueAllocatedState! + /** + * Merge states of block and it's predecessor. + * + * @param other Predecessor of this block + * @return Was this state changed? + */ public boolean meetWith(MergedBlockVerifierState other) { return this.values.mergeWith(other.getValues()); } + /** + * Verify the correspondence of original variables used in instructions + * are stored in the state of current locations. + * + * @param values Array of pairs of current location and original variable. + * @param op Operation this input array of values belongs to + * @param block Block this operation is in + * @param labelOp Label of the successor block, in-case resolution was incomplete. + */ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block, RAVInstruction.Op labelOp) { // Check that incoming values are not unknown or conflicted - these only matter if used for (int idx = 0; idx < values.count; idx++) { @@ -155,6 +172,13 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } } + /** + * Are kinds equal even when casting (LIRKindWithCast) is present? + * + * @param orig Original variable + * @param curr Current location + * @return Are they equal? + */ protected boolean kindsEqual(RAValue orig, RAValue curr) { var origKind = orig.getValue().getValueKind(); var currKind = curr.getValue().getValueKind(); @@ -170,6 +194,17 @@ protected boolean kindsEqual(RAValue orig, RAValue curr) { return currKind.equals(origKind); } + /** + * Are kinds equal even when CastValue is present? + *

+ * We need to ignore the cast value because the currently stored + * value will not be cast. + *

+ * + * @param orig Original variable + * @param fromState Value stored in state of the current location + * @return Are they equal? + */ protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { ValueKind origKind = orig.getValue().getValueKind(); ValueKind currKind = fromState.getValue().getValueKind(); @@ -180,6 +215,14 @@ protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { return origKind.equals(currKind); } + /** + * Check if alive constraint is not being violated, + * when one location is supposed to be alive after instruction + * is complete, but is used either as an output or a generic input. + * + * @param instruction Instruction with alive inputs + * @param block Block this instruction is in + */ protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock block) { for (int i = 0; i < instruction.alive.count; i++) { RAValue value = instruction.alive.curr[i]; @@ -205,6 +248,14 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock } } + /** + * Check that all instruction arrays of pairs of original variable and current location + * check out to the state stored in for this block. + * + * @param instruction Instruction we are checking + * @param block Block where it is located + * @param labelOp Label of the successor block, in-case resolution failed + */ public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstruction.Op labelOp) { if (instruction instanceof RAVInstruction.Op op) { checkInputs(op.uses, op, block, labelOp); @@ -246,26 +297,55 @@ public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstr if (!JavaKind.Object.equals(kind)) { continue; } + // Maybe check correspondence to JavaKind? + // Object -> has ref type in LIRKind? - // TODO: how to handle object kind? - // If the same virtual value is present in the register then there isn't anything to be done - // but maybe if it was changed we also maybe want to make sure that there's a pointer (Object) present? - // but for that we would need to track that information based on GC instructions + var state = this.values.get(curr); + if (state.isUnknown() || state.isConflicted()) { + continue; + } + + var instr = op.lirInstruction; + + var valueAllocState = (ValueAllocationState) state; + var source = valueAllocState.getSource(); + if (source == null) { + throw new IllegalStateException(); + } + + var v = valueAllocState.getValue(); + // How to check type is a reference? + + // Safepoint update -> change refered objects to unknown in-case GC cleared them? + // Safepoint check -> check for correspondence to JavaKind + // + checking correspondence to state } this.checkAliveConstraint(op, block); } } + /** + * Update the current state based on outputs of this instruction. + * + * @param instruction Instruction we update state from + * @param block Block where it is located + */ public void update(RAVInstruction.Base instruction, BasicBlock block) { switch (instruction) { case RAVInstruction.Op op -> this.updateWithOp(op, block); - case RAVInstruction.ValueMove virtMove -> this.updateWithVirtualMove(virtMove, block); + case RAVInstruction.ValueMove virtMove -> this.updateWithValueMove(virtMove, block); case RAVInstruction.LocationMove move -> this.values.putClone(move.to, this.values.get(move.from)); default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } } + /** + * Update the state using a generic operation + * + * @param op Operation we update state from + * @param block Block where it is located + */ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { for (int i = 0; i < op.dests.count; i++) { if (op.dests.orig[i].isIllegal()) { @@ -306,19 +386,25 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { } } - protected void updateWithVirtualMove(RAVInstruction.ValueMove virtMove, BasicBlock block) { - if (virtMove.location.getValue() instanceof RegisterValue) { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove, block)); - } else if (virtMove.location.isVariable()) { + /** + * Update state with a ValueMove. + * + * @param valueMove Value move we update state from + * @param block Block where it is located + */ + protected void updateWithValueMove(RAVInstruction.ValueMove valueMove, BasicBlock block) { + if (valueMove.location.getValue() instanceof RegisterValue) { + this.values.put(valueMove.location, new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); + } else if (valueMove.location.isVariable()) { // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD // Move before allocation // TestCase: BoxingTest.boxBoolean - var locations = this.values.getValueLocations(virtMove.variableOrConstant); + var locations = this.values.getValueLocations(valueMove.variableOrConstant); for (var location : locations) { - this.values.put(location, new ValueAllocationState(virtMove.location, virtMove, block)); + this.values.put(location, new ValueAllocationState(valueMove.location, valueMove, block)); } } else { - this.values.put(virtMove.location, new ValueAllocationState(virtMove.variableOrConstant, virtMove, block)); + this.values.put(valueMove.location, new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java index 7a9fe5b55ab0..eda397b92b88 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java @@ -3,8 +3,18 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; +/** + * No location found in an instruction after allocation for certain variable. + */ @SuppressWarnings("serial") public class MissingLocationError extends RAVError { + /** + * Construct a MissingLocationError + * + * @param instruction Instruction where violation occurred + * @param block Block where violation occurred + * @param variable Variable before allocation, that has no location afterward + */ public MissingLocationError(LIRInstruction instruction, BasicBlock block, RAValue variable) { super(MissingLocationError.getMessage(instruction, block, variable)); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java index 6d122c90b225..6c9f1cf30b18 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java @@ -1,5 +1,41 @@ package jdk.graal.compiler.lir.alloc.verifier; +/** + * Label variable location resolution method. + */ public enum PhiResolution { - FromJump, FromUsage, FromPredecessors, FromUsageGlobal, FromConflicts, ByAllocator + /** + * Before block is entered, locations are determined + * by its predecessors with state defined and variables + * used in their Jump instructions. + */ + FromJump, + /** + * By looking up variables first usage, + * and walking back to defining label - + * per each variable. + */ + FromUsage, + /** + * Resolve locations by looking at states of predecessors + * and converging on a single location. + */ + FromPredecessors, + /** + * By looking up variables first usage, + * and walking back to defining label - + * every variable at once. + */ + FromUsageGlobal, + /** + * Resolve locations by resolving + * conflicts created by numerous predecessors. + */ + FromConflicts, + /** + * Modify the allocator to keep + * label variable locations present + * after the allocation. + */ + ByAllocator } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 5553fafca35e..1731251a2daa 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -27,8 +27,18 @@ import java.util.List; import java.util.Map; +/** + * Pre-register allocation phase that needs to save information + * about variables/constants used in LIR instructions. + */ public class PreRegisterAllocationPhase extends AllocationPhase { + /** + * Factory to produce tagged constants that act like variables. + */ protected TaggedConstantFactory taggedConstantFactory; + /** + * Shared state with the verification phase. + */ protected RegisterAllocationVerifierPhaseState state; public PreRegisterAllocationPhase(RegisterAllocationVerifierPhaseState state) { @@ -36,6 +46,9 @@ public PreRegisterAllocationPhase(RegisterAllocationVerifierPhaseState state) { this.state = state; } + /** + * Overwrite a constant with a tagged one. + */ public static class ConstantOverrideValueProcedure implements ValueProcedure { private final LIR lir; private List variables; @@ -90,6 +103,13 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope } }; + /** + * Process every block and every instruction to save variables used in them for the verification procedure. + * + * @param target Machine architecture + * @param lirGenRes LIR generaration result of a method + * @param context Allocation context, used for RegisterAllocationConfig + */ @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { if (!state.shouldBeVerified(lirGenRes)) { @@ -204,8 +224,12 @@ public void doState(LIRInstruction instruction, LIRFrameState state) { * a move instruction that moves a real register value into a variable, * which is something that will always get removed from the final allocated * IR. + *

+ * This information is important to the verification process and needs to + * be part of the Verifier IR. + *

* - * @param instruction LIRInstruction we are looking at + * @param instruction LIR instruction we are looking at * @return true, if instruction is a virtual move, otherwise false */ protected boolean isVirtualMove(LIRInstruction instruction) { @@ -218,6 +242,17 @@ protected boolean isVirtualMove(LIRInstruction instruction) { return (input instanceof RegisterValue || input instanceof StackSlot /*|| input instanceof AbstractAddress*/) && LIRValueUtil.isVariable(valueMov.getResult()); } + /** + * Determines if a move is speculative - it could potentially be + * removed, but hold important information to the verification process. + *

+ * For example, this happens for a move between two variables and after + * allocation locations are equal, making the move redundant. + *

+ * + * @param instruction LIR instruction we are looking at + * @return true, if instruction is a speculative move, otherwise false + */ protected boolean isSpeculativeMove(LIRInstruction instruction) { if (!instruction.isValueMoveOp()) { return false; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java index 1763929246bb..61e006afe915 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java @@ -1,8 +1,14 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.debug.GraalError; + +/** + * An internal error occurred within the + * verification process, not caused by + * the Register Allocator. + */ @SuppressWarnings("serial") -public class RAVError extends Error { - // These shouldn't happen within the verifier +public class RAVError extends GraalError { public RAVError(String message) { super(message); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java index 1a7e3f3e3feb..37dfc88fdbe4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java @@ -1,7 +1,14 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.debug.GraalError; + +/** + * Register Allocation Verification Exception - + * a violation made by the Register Allocator occurred + * and will be thrown in verification phase. + */ @SuppressWarnings("serial") -public class RAVException extends RuntimeException { +public class RAVException extends GraalError { public RAVException(String message) { super(message); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java index a58239a07f8c..24577d2e4346 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -2,6 +2,11 @@ import java.util.List; +/** + * Composite exception taking every Register Allocation Verification + * exception that occurred (exceptions done by the Register Allocator) + * and combining them together to one exception. + */ @SuppressWarnings("serial") public class RAVFailedVerificationException extends RAVException { public RAVFailedVerificationException(String compUnitName, List exceptions) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 32ca489b7748..96f496880cc2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -13,9 +13,38 @@ import java.util.List; public class RAVInstruction { - public static class Base { + /** + * Base class for all RAV instructions + */ + public abstract static class Base { + /** + * Underlying LIR instruction that this is representing. + */ protected LIRInstruction lirInstruction; + + /** + * List of virtual moves to be inserted after this instruction, + * virtual moves are ones removed by the allocator still holding + * relevant information to the verification process, for example + * first label always uses registers (instead of variables) based on ABI, + * and a move is inserted indicating that those registers have certain + * variables. + * + * @example [rsi, rbp] = LABEL + * v1 = MOVE rsi + */ protected List virtualMoveList; + + /** + * List of speculative moves, these might be removed, but still + * hold important information for us, so we add them to the verifier + * IR in-case they are, this happens when a MOVE source and target + * locations are equal (and thus redundant) but before allocation + * their variable counter-parts are not equal. + * + * @example before alloc: v1 = MOVE v2 + * after alloc: rax = MOVE rax + */ protected List speculativeMoveList; public Base(LIRInstruction lirInstruction) { @@ -45,9 +74,21 @@ public List getSpeculativeMoveList() { } } + /** + * Helper class to handle pairs of locations and variables. + */ public static class ValueArrayPair { + /** + * Array of size count holding original variables before allocation. + */ protected RAValue[] orig; + /** + * Array of size count holding current locations used after allocation. + */ protected RAValue[] curr; + /** + * Number of pairs of values stored here. + */ protected int count; public ValueArrayPair(int count) { @@ -140,15 +181,45 @@ public String toString() { } } + /** + * RAV instruction that handles a regular operation + * in an abstract way - we do not care about the function of said operation. + */ public static class Op extends Base { + /** + * Pairs of outputs generated by this operation + */ public ValueArrayPair dests; + /** + * Pairs of inputs used by this operation + */ public ValueArrayPair uses; + /** + * Pairs of temporaries used by this operation. + */ public ValueArrayPair temp; + /** + * Pairs of inputs used by this operation + * that need to be alive after it completes. + */ public ValueArrayPair alive; + /** + * JavaKinds retrieved from LIRFrameState for + * the stateValues. + */ public JavaKind[] kinds; + + /** + * Pairs of values retrieved from LIRFrameState, + * verified same as any other input to make + * sure GC has all necessary information. + */ public ValueArrayPair stateValues; + /** + * Count number of values stored. + */ private final class GetCountProcedure implements InstructionValueProcedure { private int count = 0; @@ -169,6 +240,8 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope public Op(LIRInstruction instruction) { super(instruction); + // We first count the number of entries, then allocate + // an array of said size for both current locations and original variables. var countValuesProc = new GetCountProcedure(); instruction.forEachInput(countValuesProc); @@ -202,6 +275,10 @@ public String toString() { } } + /** + * A move between to concrete locations + * inserted by the register allocator. + */ public static class LocationMove extends Base { public RAValue from; public RAValue to; @@ -217,9 +294,10 @@ public String toString() { } } + /** + * Move between two registers + */ public static class RegMove extends LocationMove { - // public RegisterValue from; - // public RegisterValue to; public RAValue from; public RAValue to; @@ -234,8 +312,9 @@ public String toString() { } } - // StackMove class to handle STACKMOVE instruction, temporary for now - // before I decide how all Moves should be handled + all possible combinations. + /** + * Move between two stack slots (virtual or not). + */ public static class StackMove extends LocationMove { public RAValue from; public RAValue to; @@ -255,6 +334,9 @@ public String toString() { } } + /** + * Reload symbol from stack slot to a register. + */ public static class Reload extends LocationMove { public RAValue from; public RAValue to; @@ -270,6 +352,9 @@ public String toString() { } } + /** + * Spill symbol from register to a spill slot. + */ public static class Spill extends LocationMove { public RAValue from; public RAValue to; @@ -285,8 +370,19 @@ public String toString() { } } + /** + * Move a value or variable to a concrete location, + * inserted by allocator (materialization) or not (virtual move). + */ public static class ValueMove extends Base { + /** + * Constant generated by materialization or variable from virtual/speculative move. + */ public RAValue variableOrConstant; + + /** + * Where variable (or constant) is being stored + */ public RAValue location; // Can also be another variable! public ValueMove(LIRInstruction instr, Value variableOrConstant, Value location) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index 499966e70d98..953f943a90d0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -4,7 +4,23 @@ import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.meta.Value; +/** + * Wrapper around Value to change how indexing + * in data structures like Map or Set is done. + *

+ * Register is indexed by its name without the kind. + * Variable is indexed by its number index without the kind. + * Stack slots and constants remain as is, because the kind + * does not mess with indexing. + *

+ */ public class RAValue { + /** + * Create a new RAValue instance from Value. + * + * @param value Value we are wrapping + * @return Instance of RAValue + */ public static RAValue create(Value value) { if (LIRValueUtil.isVariable(value)) { return new RAVariable(LIRValueUtil.asVariable(value)); @@ -44,6 +60,13 @@ public int hashCode() { return this.value.hashCode(); } + /** + * Equal RegisterValue on it's Register, not Register and kind, + * otherwise same as Value. + * + * @param other The reference object with which to compare. + * @return Are said values equal? + */ @Override public boolean equals(Object other) { if (other instanceof RAValue otherValueWrap) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java index c39088590f84..a59d946ba2cd 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java @@ -2,6 +2,14 @@ import jdk.graal.compiler.lir.Variable; +/** + * Wrapper around Variable to change how indexing + * in data structures like Map or Set is done. + *

+ * We index only by the Variable index instead of + * including the kind as well. + *

+ */ public class RAVariable extends RAValue { protected Variable variable; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index a79223c06de6..8ce34d5bce03 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -10,20 +10,60 @@ import java.util.List; import java.util.Queue; +/** + * Class encapsulating the whole Register Allocation Verification. + * Maintaining entry states for blocks, resolving label variable + * locations and checking validity of every location to variable + * correspondence. + */ public class RegisterAllocationVerifier { + /** + * Verifier IR that abstracts LIR instructions + * and marks moves inserted by the allocator. + */ protected BlockMap> blockInstructions; + + /** + * Current state of said block during processing. + */ protected BlockMap blockStates; + + /** + * State of the block on entry, calculated from its predecessors. + */ protected BlockMap blockEntryStates; + /** + * LIR necessary for to access the program graph. + */ protected LIR lir; + + /** + * Register Allocator config used for validating + * if valid register is used by the allocator. + */ protected RegisterAllocationConfig registerAllocationConfig; + + /** + * Resolution method used for determining + * label variable locations. + */ protected PhiResolution phiResolution; protected FromPredecessorsResolver fromPredecessorsResolver; protected FromJumpResolver fromJumpResolver; protected FromUsageResolver fromUsageResolver; + + /** + * Resolves locations for label variables by finding + * their first usage and walking back to the defining + * label. + */ protected FromUsageResolverGlobal fromUsageResolverGlobal; + /** + * Conflict resolver for re-materialized constants. + */ protected ConflictResolver constantMaterializationConflictResolver; protected ConflictResolver labelConflictResolver; @@ -47,6 +87,13 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); } + /** + * Do all predecessors have state defined for this block? + * Meaning they were processed by the entry block calculation. + * + * @param block Block for which we look at predecessors + * @return true, if all predecessors of said block have a state defined. + */ private boolean doPrecessorsHaveStates(BasicBlock block) { for (int i = 0; i < block.getPredecessorCount(); i++) { var pred = block.getPredecessorAt(i); @@ -57,6 +104,13 @@ private boolean doPrecessorsHaveStates(BasicBlock block) { return true; } + /** + * Does this instruction have locations missing + * in it's output array pair? + * + * @param op Operation we are looking at - a label + * @return true, if there is at least one missing location, otherwise false. + */ private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { return op.hasMissingDefinitions(); } @@ -191,10 +245,7 @@ private void addMissingLabelBlocks(Queue> worklist) { } /** - * Here, use block entries to check if - * inputs to instructions are correct. - * - * @return true, if valid, otherwise false + * Verify every instruction input. */ public void verifyInstructionInputs() { for (var blockId : this.lir.getBlocks()) { @@ -214,6 +265,11 @@ public void verifyInstructionInputs() { } } + /** + * Verify every instruction and collect every exception that has occurred. + * + * @param compUnitName Name of this compilation unit, we are verifying + */ public void verifyInstructionsAndCollectErrors(String compUnitName) { List exceptions = new ArrayList<>(); for (var blockId : this.lir.getBlocks()) { @@ -237,6 +293,11 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { } } + /** + * Run the verification process, including label variable + * resolution, handling of materialized constants, calculating + * entry states for every block. + */ public void run() { this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); if (this.phiResolution == PhiResolution.FromConflicts) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 845d72bf787b..de1a71c11627 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -27,6 +27,9 @@ import java.util.Map; import java.util.Set; +/** + * Verification phase for Register Allocation + */ public class RegisterAllocationVerifierPhase extends AllocationPhase { public static class Options { @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) @@ -42,12 +45,19 @@ public static class Options { public static final OptionKey RAFilter = new OptionKey<>(null); } + /** + * Shared phase state + */ private final RegisterAllocationVerifierPhaseState state; public RegisterAllocationVerifierPhase(RegisterAllocationVerifierPhaseState state) { this.state = state; } + public RegisterAllocationVerifierPhaseState getState() { + return state; + } + @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { if (!state.shouldBeVerified(lirGenRes)) { @@ -85,6 +95,13 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo } } + /** + * Process instructions after allocation and create the Verifier IR. + * Using previously stored instructions from the PreAlloc phase. + * + * @param lirGenRes LIR generation result of this method + * @return Verifier IR + */ protected BlockMap> getVerifierInstructions(LIRGenerationResult lirGenRes) { var lir = lirGenRes.getLIR(); var preallocMap = state.getInstructionMap(lirGenRes); @@ -174,7 +191,7 @@ protected BlockMap> getVerifierInstructions(LIRGenerat * Create Register Verifier Instruction that was created by the Register Allocator. * Generally speaking, it's always a move instruction, other ones return null. * - * @param instruction LIRInstruction newly created by Register Allocator + * @param instruction LIR Instruction newly created by Register Allocator * @return Spill, Reload, Move or null if instruction is not a move */ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { @@ -184,8 +201,7 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) var constant = constatLoad.getConstant(); var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot - // This isn't really a virtual move, but it currently acts the same, so we keep it, - // we take constants as variables. + // Constant materialization result return new RAVInstruction.ValueMove(instruction, new ConstantValue(result.getValueKind(), constant), result); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java index 8afdc0547c36..d3a968da2b30 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -7,11 +7,18 @@ import java.util.IdentityHashMap; import java.util.Map; +/** + * Phase state shared by the preallocation and verification phases, + * pertaining mostly to shared config and Verifier IR. + */ public class RegisterAllocationVerifierPhaseState { public PhiResolution phiResolution; public boolean moveConstants; public String filterStr; + /** + * Mapping between LIRGenerationResult and Map of LIR instructions to Verifier instructions. + */ protected Map> verifierInstructions; public RegisterAllocationVerifierPhaseState(OptionValues options) { @@ -22,6 +29,13 @@ public RegisterAllocationVerifierPhaseState(OptionValues options) { this.filterStr = RegisterAllocationVerifierPhase.Options.RAFilter.getValue(options); } + /** + * Should this method be verified? Filter when filterStr is set, + * use ful debugging purposes. + * + * @param lirGenRes LIR generation result describing the method + * @return true, if method should be verified, otherwise false + */ public boolean shouldBeVerified(LIRGenerationResult lirGenRes) { var compUnitName = lirGenRes.getCompilationUnitName(); // Filter for compilation unit substring to run verification only on @@ -30,12 +44,24 @@ public boolean shouldBeVerified(LIRGenerationResult lirGenRes) { return filterStr == null || compUnitName.contains(filterStr); } + /** + * Create a new instruction map for this method. + * + * @param lirGenRes LIR generation result of this method + * @return New instruction map + */ public Map createInstructionMap(LIRGenerationResult lirGenRes) { var idMap = new IdentityHashMap(); this.verifierInstructions.put(lirGenRes, idMap); return idMap; } + /** + * Retrieve an existing instruction map for this method. + * + * @param lirGenRes LIR generation result of this method + * @return Old instruction map + */ public Map getInstructionMap(LIRGenerationResult lirGenRes) { if (!this.verifierInstructions.containsKey(lirGenRes)) { throw new IllegalStateException(); @@ -44,6 +70,12 @@ public Map getInstructionMap(LIRGenerationR return this.verifierInstructions.get(lirGenRes); } + /** + * Delete existing instruction map for this method after + * is it is no longer needed. + * + * @param lirGenRes LIR generation result of this method + */ public void deleteInstructionMap(LIRGenerationResult lirGenRes) { verifierInstructions.remove(lirGenRes); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java index e10c800d687c..0e007bd8a7ef 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java @@ -4,6 +4,9 @@ import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; +/** + * Factory to create a tagged constant from an existing constant. + */ public class TaggedConstantFactory { protected int lastTag; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java index d233a78dbf51..55455a63a205 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java @@ -4,6 +4,10 @@ import java.nio.ByteBuffer; +/** + * Data pointer constant acting as a variable (based on its tag) + * to make resolving from jump it easier. + */ public class TaggedDataPointerConstant extends DataPointerConstant { protected DataPointerConstant constant; protected int tag; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java index 9d20be648f7d..a480e346dde0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java @@ -3,6 +3,10 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; +/** + * Java constant acting as a variable (based on its tag) to + * make resolving from jump it easier. + */ public class TaggedJavaConstant implements JavaConstant { protected JavaConstant constant; protected int tag; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java index 747ae55554e9..bc4a34dac2ea 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java @@ -2,6 +2,10 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; +/** + * Target location was overwritten when looking for + * label variable locations by their usage. + */ @SuppressWarnings("serial") public class TargetLocationOverwrittenException extends RAVException { public RAVInstruction.ValueMove valueMove; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index 2302cf3bda1a..07d2fef8ce67 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -1,6 +1,13 @@ package jdk.graal.compiler.lir.alloc.verifier; +/** + * Default allocation state for all locations, + * nothing was yet inserted. + */ public class UnknownAllocationState extends AllocationState { + /** + * Single instance used for all occurrences of Unknown state. + */ public static UnknownAllocationState INSTANCE = new UnknownAllocationState(); @Override @@ -8,6 +15,13 @@ public boolean isUnknown() { return true; } + /** + * Meet state from predecessor, if both are unknown then unknown is returned, + * otherwise conflict occurs. + * + * @param other Other state coming from a predecessor edge + * @return Unknown if both are, otherwise a conflict + */ @Override public AllocationState meet(AllocationState other) { if (other.isUnknown()) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java index 243a0a2e95d4..f42c758dc5d5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java @@ -4,6 +4,11 @@ import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.StandardOp; +/** + * Unknown instruction was found after the allocation, + * this usually means that it's a different instruction + * from a Move, and we do not know how to handle it. + */ @SuppressWarnings("serial") public class UnknownInstructionError extends RAVError { public LIRInstruction instruction; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index b040b7445051..ead3fb3a0b88 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -9,6 +9,10 @@ import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.Value; +/** + * Allocation state holding a single, concrete Value (wrapped with RAValue), + * also accompanied by instruction and block where it was created. + */ public class ValueAllocationState extends AllocationState implements Cloneable { protected RAValue value; protected RAVInstruction.Base source; @@ -17,6 +21,8 @@ public class ValueAllocationState extends AllocationState implements Cloneable { public ValueAllocationState(RAValue raValue, RAVInstruction.Base source, BasicBlock block) { var value = raValue.getValue(); if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { + // Here, we make sure that no new value class is used here, without consideration. + // StackSlot, RegisterValue is present in start block in label as predefined argument // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS // ConstantValue act as Variable @@ -37,6 +43,13 @@ public ValueAllocationState(ValueAllocationState other) { this.block = other.getBlock(); } + /** + * Create an illegal value allocation state, used + * as a substitute for Unknown state when creating + * a conflict. + * + * @return instance of ValueAllocationState holding Value.ILLEGAL. + */ public static ValueAllocationState createIllegal() { // TODO: pass in the block that created this value return new ValueAllocationState(new RAValue(Value.ILLEGAL), null, null); @@ -58,8 +71,19 @@ public BasicBlock getBlock() { return block; } + /** + * Meet a state from predecessor block, if it's ValueAllocationState + * and contents are equal, then same state is returned, otherwise + * a conflict is created between said states. + * + * @param other Other state coming from a predecessor edge + * @return ValueAllocationState if their contents are equal, otherwise ConflictedAllocationState. + */ public AllocationState meet(AllocationState other) { if (other.isUnknown()) { + // Unknown is coming from different predecessor where this location + // is undefined, meaning this value is not always accessible in the successor + // and thus conflict is created. return new ConflictedAllocationState(createIllegal(), this); } @@ -71,7 +95,7 @@ public AllocationState meet(AllocationState other) { } var otherValueAllocState = (ValueAllocationState) other; - if (!this.value.equals(otherValueAllocState.getRAValue())) { + if (!this.value.equals(otherValueAllocState.getRAValue())) { // Does not take kind into account. return new ConflictedAllocationState(this, otherValueAllocState); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index c48ec7c84f32..3999f631fd60 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -3,6 +3,9 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; +/** + * Value was not found in the location we needed it in. + */ @SuppressWarnings("serial") public class ValueNotInRegisterException extends RAVException { public LIRInstruction instruction; @@ -11,6 +14,15 @@ public class ValueNotInRegisterException extends RAVException { public RAValue location; // Can be StackSlot, RegisterValue or memory public AllocationState state; + /** + * Construct a ValueNotInRegisterException + * + * @param instruction Instruction where violation occurred + * @param block Block where violation occurred + * @param variable Target varible we are looking for + * @param location Location where we couldn't find it + * @param state The actual state that the location is in + */ public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock block, RAValue variable, RAValue location, AllocationState state) { super(ValueNotInRegisterException.getErrorMessage(instruction, block, variable, location, state)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index 51355b0a9d60..f0a0868ac93c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -7,6 +7,14 @@ import java.util.List; public class VerifierPrinter { + /** + * Print human-readable representation of the Verifier IR + * to an output stream. + * + * @param out Output stream + * @param lir LIR + * @param instructions Verifier IR + */ public static void print(PrintStream out, LIR lir, BlockMap> instructions) { for (var blockId : lir.getBlocks()) { var block = lir.getBlockById(blockId); From 89478c1dd7ce6958d86bb26fa97ca40637bdf520 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 18 Feb 2026 11:57:31 +0100 Subject: [PATCH 051/112] Do not use IllegalStateException --- ...nstantMaterializationConflictResolver.java | 2 +- .../verifier/FromUsageResolverGlobal.java | 3 ++- .../RegisterAllocationVerifierPhaseState.java | 3 ++- ...aterializedConstantSourceMissingError.java | 20 +++++++++++++++++++ .../alloc/verifier/TaggedConstantFactory.java | 3 ++- 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 610c7f4bf0d9..276555d667da 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -175,7 +175,7 @@ protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllo var location = move.location.getValue(); return location instanceof StackSlot || location instanceof VirtualStackSlot; } else { - throw new IllegalStateException(); + throw new RematerializedConstantSourceMissingError(source, variable); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 6c1637aa7968..46c8f959032a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -2,6 +2,7 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.util.EconomicHashMap; @@ -280,7 +281,7 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc var location = usage.locations.get(variable); if (location == null || location.isIllegal()) { - throw new IllegalStateException(); + GraalError.shouldNotReachHere("Location is " + location + " when resolving " + variable + " should not happen."); } label.dests.curr[i] = location; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java index d3a968da2b30..085435b0dcc2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -1,5 +1,6 @@ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.options.OptionValues; @@ -64,7 +65,7 @@ public Map createInstructionMap(LIRGenerati */ public Map getInstructionMap(LIRGenerationResult lirGenRes) { if (!this.verifierInstructions.containsKey(lirGenRes)) { - throw new IllegalStateException(); + GraalError.shouldNotReachHere("PreAlloc phase did not run for " + lirGenRes.getCompilationUnitName()); } return this.verifierInstructions.get(lirGenRes); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java new file mode 100644 index 000000000000..25e4842e9528 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java @@ -0,0 +1,20 @@ +package jdk.graal.compiler.lir.alloc.verifier; + +/** + * Re-materialized constant has wrong source (not a Value Move), + * but either undefined or something different. + */ +@SuppressWarnings("serial") +public class RematerializedConstantSourceMissingError extends RAVError { + public RematerializedConstantSourceMissingError(RAVInstruction.Base instruction, RAVariable variable) { + super(variable + " was materialized to " + instruction); + } + + public static String getErrorMessage(RAVInstruction.Base instruction, RAVariable variable) { + if (instruction == null) { + return "Variable " + variable + " has no materialization source."; + } + + return "Variable " + variable + " was materialized to " + instruction; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java index 0e007bd8a7ef..635c95083690 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java @@ -1,6 +1,7 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.type.DataPointerConstant; +import jdk.graal.compiler.debug.GraalError; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; @@ -22,7 +23,7 @@ public Constant createNew(Constant constant) { return switch (constant) { case JavaConstant javaConst -> this.createNew(javaConst); case DataPointerConstant ptrConst -> this.createNew(ptrConst); - default -> throw new IllegalStateException(); + default -> throw new GraalError("Unhandled constant type"); }; } From 37dc5e37a7b7b5cd92441fb727426a190c53700a Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 18 Feb 2026 13:45:31 +0100 Subject: [PATCH 052/112] Check if variable and its alias are equal --- .../verifier/CircularDefinitionError.java | 62 ---- .../lir/alloc/verifier/FromJumpResolver.java | 107 ------- .../verifier/FromPredecessorsResolver.java | 282 ----------------- .../lir/alloc/verifier/FromUsageResolver.java | 285 ------------------ .../verifier/FromUsageResolverGlobal.java | 11 +- .../alloc/verifier/LabelConflictResolver.java | 152 ---------- .../alloc/verifier/TaggedConstantFactory.java | 37 --- .../verifier/TaggedDataPointerConstant.java | 58 ---- .../alloc/verifier/TaggedJavaConstant.java | 91 ------ .../TargetLocationOverwrittenException.java | 23 -- 10 files changed, 6 insertions(+), 1102 deletions(-) delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java deleted file mode 100644 index 41fe01d5596a..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CircularDefinitionError.java +++ /dev/null @@ -1,62 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; - -import java.util.List; - -/** - * Circular definition was created between - * a label block and one of its predecessors, - * meaning a variable defined in the first block - * is being propagated back to it by one of its - * predecessors, which is an invalid state. - *

- * Defining block's entry state would contain - * the variable it is defining in its label. - *

- */ -@SuppressWarnings("serial") -public class CircularDefinitionError extends RAVError { - /** - * Construct a CircularDefinitionError - * - * @param defBlock Block where label variables are first defined - * @param predecessor The predecessor block creating the circular definition - * @param label Label defining variables - * @param beingPropagated Which variables are being propagated to defBlock from the predecessor - */ - public CircularDefinitionError(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { - super(getErrorMessage(defBlock, predecessor, label, beingPropagated)); - } - - static String getErrorMessage(BasicBlock defBlock, BasicBlock predecessor, RAVInstruction.Op label, List beingPropagated) { - StringBuilder operandString = new StringBuilder("["); - var values = label.dests; - for (int i = 0; i < values.count; i++) { - if (!values.orig[i].isVariable()) { - continue; // Avoid fatal error - } - - var variable = values.orig[i].asVariable(); - if (!beingPropagated.contains(variable)) { - continue; - } - - var location = values.curr[i]; - - operandString.append(variable.toString()); - if (location != null) { - operandString.append(" -> ").append(location); - } else { - operandString.append(" -> ?"); - } - - operandString.append(", "); - } - - operandString.setLength(operandString.length() - 2); - operandString.append("]"); - - return "Circular definition for variable detected " + predecessor + " -> " + defBlock + " with " + operandString; - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java deleted file mode 100644 index 508f6988f818..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromJumpResolver.java +++ /dev/null @@ -1,107 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; - -import java.util.List; -import java.util.Set; - -/** - * Resolve label variable location before we jump to - * said block from its predecessors by looking at - * their state and finding said variables in their - * JumpOp. - */ -public class FromJumpResolver { - public LIR lir; - public BlockMap> blockInstructions; - public BlockMap blockStates; - - public FromJumpResolver(LIR lir, BlockMap> blockInstructions, BlockMap blockStates) { - this.lir = lir; - this.blockInstructions = blockInstructions; - this.blockStates = blockStates; - } - - /** - * Resolve label variables to said block from their state - * and their jump variables. - *

- * If multiple locations are chosen, we take the last one - *

- * - * @param block Target block - */ - public void resolvePhi(BasicBlock block) { - var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(block).getFirst(); - for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - if (state == null) { - continue; - } - - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - var inputValue = jump.alive.orig[i]; - - var varLoc = state.values.getValueLocations(inputValue); - if (locations == null) { - locations = varLoc; - continue; - } - - locations.retainAll(varLoc); - } - - if (locations == null) { - continue; - } - - RAValue location = null; - if (locations.size() != 1) { - if (locations.isEmpty()) { - return; - } - - // Selects the location that was used most recently, - // but it has to be used recently by all the predecessors. - for (int j = 0; j < block.getPredecessorCount(); j++) { - int time = -1; - RAValue blockReg = null; - for (var loc : locations) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - if (state == null) { - continue; - } - - var regTime = state.values.getKeyTime(loc); - if (regTime > time) { - time = regTime; // Max time - blockReg = loc; - } - } - - if (location == null) { - location = blockReg; - } else if (!location.equals(blockReg)) { - // Not same for all blocks, so none choosen. - return; - } - } - } else { - location = locations.stream().findFirst().get(); - } - - labelInstr.dests.curr[i] = location; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = location; - } - } - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java deleted file mode 100644 index 9e5083c50130..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromPredecessorsResolver.java +++ /dev/null @@ -1,282 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.util.EconomicHashMap; -import jdk.graal.compiler.util.EconomicHashSet; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - -/** - * Resolve label variable locations by cross-referencing - * state from its predecessors and settling on a single - * location for every variable. - */ -public class FromPredecessorsResolver { - public LIR lir; - public BlockMap> blockInstructions; - public BlockMap blockStates; // Current states - public BlockMap blockEntryStates; // State on entry to block - - public FromPredecessorsResolver(LIR lir, BlockMap> blockInstructions, BlockMap blockStates, BlockMap blockEntryStates) { - this.lir = lir; - this.blockInstructions = blockInstructions; - this.blockStates = blockStates; - this.blockEntryStates = blockEntryStates; - } - - /** - * Fill in missing variable locations for current block's label instruction and predecessor - * jump instructions. - *

- * We are looking for intersection between locations of individual predecessors, this should - * give us a single register that is used for the phi, and is necessary for jump to verify - * that contents are alive that that point and for label to define where the result of phi - * will be held to used in said block. - * - * @param block Block that needs phi function output - * @param labelInstr Label instruction of said block - */ - public boolean resolvePhiFromPredecessors(BasicBlock block, RAVInstruction.Op labelInstr) { - for (int i = 0; i < labelInstr.dests.count; i++) { - Set locations = null; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - var inputValue = jump.alive.orig[i]; - - var varLoc = state.values.getValueLocations(inputValue); - if (locations == null) { - locations = varLoc; - continue; - } - - locations.retainAll(varLoc); - } - - RAValue location = null; - if (locations.size() != 1) { - if (locations.isEmpty()) { - return false; - } - - // Selects the location that was used most recently, - // but it has to be used recently by all the predecessors. - for (int j = 0; j < block.getPredecessorCount(); j++) { - int time = -1; - RAValue blockReg = null; - for (var loc : locations) { - var pred = block.getPredecessorAt(j); - var state = this.blockStates.get(pred); - - var regTime = state.values.getKeyTime(loc); - if (regTime > time) { - time = regTime; // Max time - blockReg = loc; - } - } - - if (location == null) { - location = blockReg; - } else if (!location.equals(blockReg)) { - // Not same for all blocks, so none choosen. - return false; - } - } - } else { - location = locations.stream().findFirst().get(); - } - - labelInstr.dests.curr[i] = location; - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = location; - } - } - - return true; - } - - /** - * Propagate newly defined label variable locations to all successors - * of said block, this needs to be because successor state still contain - * old locations and were already processed. - *

- * TODO: incorporate new meet logic here - * - * @param defBlock Block defining new variable locations with its label. - */ - public void propagateLabelChangeFromPredecessors(BasicBlock defBlock) { - var labelInstr = (RAVInstruction.Op) this.blockInstructions.get(defBlock).getFirst(); - - // Definition block needs to have this set. - var propagateMap = new EconomicHashMap, List>(); - var locationMap = new EconomicHashMap, Map>>(); - - var defVariableToLocations = new EconomicHashMap>(); - var defBlockVariablesToPropagate = new ArrayList(); - for (int i = 0; i < labelInstr.dests.count; i++) { // Init locations to propagate by label variables - var register = labelInstr.dests.curr[i]; - var variable = labelInstr.dests.orig[i].asVariable(); - - defBlockVariablesToPropagate.add(variable); - - var variableLocationList = new EconomicHashSet(); - variableLocationList.add(register); - defVariableToLocations.put(variable, variableLocationList); - } - - Queue> worklist = new LinkedList<>(); - Set> processed = new EconomicHashSet<>(); - worklist.add(defBlock); - propagateMap.put(defBlock, defBlockVariablesToPropagate); - locationMap.put(defBlock, defVariableToLocations); - - while (!worklist.isEmpty()) { - var curr = worklist.remove(); - if (processed.contains(curr)) { - continue; - } - processed.add(curr); - - var state = this.blockStates.get(curr); - var variablesToPropagate = propagateMap.get(curr); - var variableToLocations = locationMap.get(curr); - - var instructions = blockInstructions.get(curr); - for (var instruction : instructions) { - if (curr.equals(defBlock) && instruction.lirInstruction instanceof StandardOp.LabelOp) { - continue; - } - - RAValue fromLocation; - RAValue toLocation; - - switch (instruction) { - case RAVInstruction.ValueMove virtMove -> { - toLocation = virtMove.location; - fromLocation = null; - } - case RAVInstruction.LocationMove move -> { - fromLocation = move.from; - toLocation = move.to; - } - case RAVInstruction.Op op -> { - for (int i = 0; i < op.dests.count; i++) { - var location = op.dests.curr[i]; - if (location == null) { - continue; - } - - var itToPropagate = variablesToPropagate.iterator(); - while (itToPropagate.hasNext()) { - var variable = itToPropagate.next(); - var locations = variableToLocations.get(variable); - locations.remove(location); // Location overwritten, no need to distribute further. - } - } - - continue; - } - default -> { - continue; - } - } - - var itToPropagate = variablesToPropagate.iterator(); - while (itToPropagate.hasNext()) { - var variable = itToPropagate.next(); - var locations = variableToLocations.get(variable); - if (fromLocation != null && locations.contains(fromLocation)) { - locations.add(toLocation); // New location to propagate - } else if (locations.contains(toLocation)) { - locations.remove(toLocation); // Overwritten - } - } - } - - var variablesToBePropagated = new ArrayList(); - var iterator = variablesToPropagate.iterator(); - while (iterator.hasNext()) { - var variable = iterator.next(); - var locations = variableToLocations.get(variable); - if (locations.isEmpty()) { - continue; - } - - variablesToBePropagated.add(variable); - for (var location : locations) { - if (state != null) { // State of successor blocks also needs to contain these, if it was not overwritten. - state.values.put(location, new ValueAllocationState(variable, labelInstr, defBlock)); - } - } - } - - if (variablesToBePropagated.isEmpty()) { - continue; - } - - for (int i = 0; i < curr.getSuccessorCount(); i++) { - var succ = curr.getSuccessorAt(i); - var succEntryState = this.blockEntryStates.get(succ); - if (succEntryState == null) { - continue; - } - - if (succ.equals(defBlock)) { - // This means that the definition block would have same value as predecessor - // for example: B0 defines [v0] in label, B1 is it's successor as well as it's - // predecessor, and if it does not overwrite this register, it would change - // entry state for B0 to include v0, which is defined by B0. - - for (int j = 0; j < labelInstr.dests.count; j++) { - var variable = labelInstr.dests.orig[j]; - - if (!variablesToPropagate.contains(variable)) { - continue; - } - - var location = labelInstr.dests.curr[j]; - var locations = variableToLocations.get(variable); - if (locations.contains(location)) { - // Only throw this error if location in label was not overwritten. - throw new CircularDefinitionError(defBlock, curr, labelInstr, variablesToBePropagated); - } - } - - continue; - } - - boolean dominates = succ.dominates(defBlock); - if (dominates) { - continue; - } - - Map> newLoc = new EconomicHashMap<>(); - var itToBePropagated = variablesToBePropagated.iterator(); - while (itToBePropagated.hasNext()) { - var variable = itToBePropagated.next(); - var locations = variableToLocations.get(variable); - for (var location : locations) { // Update entry state for new predecessors! - succEntryState.values.put(location, new ValueAllocationState(variable, labelInstr, defBlock)); - } - - newLoc.put(variable, new EconomicHashSet<>(locations)); - } - - locationMap.put(succ, newLoc); - propagateMap.put(succ, variablesToBePropagated); - worklist.add(succ); - } - } - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java deleted file mode 100644 index 4316ae765a15..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolver.java +++ /dev/null @@ -1,285 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.util.EconomicHashMap; -import jdk.graal.compiler.util.EconomicHashSet; - -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - -/** - * Resolve label variable locations by finding their first usage - * and going in-reverse back to the label to handle any moves - * in-between the label and usage, selecting a single location. - */ -class FromUsageResolver { - protected LIR lir; - protected BlockMap> blockInstructions; - - public Map labelMap; - public Map variableRegisterMap; - public Map usageAliasMap; - - class PathEntry { - public BasicBlock block; - public PathEntry previous; - - PathEntry(BasicBlock block) { - this.block = block; - this.previous = null; - } - - PathEntry(BasicBlock block, PathEntry previous) { - this.block = block; - this.previous = previous; - } - - public PathEntry next(BasicBlock block) { - return new PathEntry(block, this); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o instanceof PathEntry le && le.block.equals(this.block)) { - if (le.previous == null) { - return this.previous == null; - } - - return le.previous.block.equals(this.previous.block); - } - - return false; - } - - @Override - public int hashCode() { - return this.previous == null ? block.hashCode() : block.hashCode() ^ previous.block.hashCode(); - } - - @Override - public String toString() { - return (previous == null ? "" : previous.toString()) + " -> " + block.toString(); - } - } - - protected FromUsageResolver(LIR lir, BlockMap> blockInstructions) { - this.lir = lir; - this.blockInstructions = blockInstructions; - - this.labelMap = new EconomicHashMap<>(); - this.variableRegisterMap = new EconomicHashMap<>(); - this.usageAliasMap = new EconomicHashMap<>(); - } - - private RAValue getLocationFromUsage(PathEntry path, BasicBlock defBlock, RAVInstruction.Base usageInstruction, RAValue location, RAValue variable) { - boolean reachedUsage = false; - while (true) { // TERMINATION ARGUMENT: Iterates over linked list using PathEntry nodes - // Either we get an assertion about path being null or reaching def block - which always happens - assert path != null; - - var block = path.block; - var instructions = this.blockInstructions.get(block).reversed(); - for (var instruction : instructions) { - if (instruction == usageInstruction) { - reachedUsage = true; - continue; - } - - if (!reachedUsage) { - continue; - } - - // Tracking the value bottom up, from the usage up to the label definition - // looking for any changes to the target register that could highlight - // different register is supposed to be used, in case of reload/spill combo or - // register move. If we are wrong about the target register then, it will - // get thrown out in the verification stage. - switch (instruction) { - case RAVInstruction.ValueMove move -> { - if (move.location.equals(location) && !move.variableOrConstant.equals(variable)) { - throw new TargetLocationOverwrittenException(move, block); - } - } - case RAVInstruction.LocationMove move -> { - if (move.to.equals(location)) { - location = move.from; - } - } - // For Op, if there is a redefinition, we let the later stages handle that - default -> { - } - } - } - - if (path.block.equals(defBlock)) { - break; - } - - path = path.previous; - } - - return location; - } - - private void mapLabelVariableFromUsage( - // @formatter:off - Map> labelToBlockMap, - RAVariable variable, RAValue location, - PathEntry path, RAVInstruction.Base useInstruction) { - // @formatter:on - var variableLabelInstr = this.labelMap.get(variable); - if (variableLabelInstr == null) { - return; - } - - var labelBlock = labelToBlockMap.get(variableLabelInstr); - var register = this.getLocationFromUsage(path, labelBlock, useInstruction, location, variable); - - this.variableRegisterMap.put(variable, register); - this.labelMap.remove(variable); - this.usageAliasMap.remove(variable); - - for (int j = 0; j < variableLabelInstr.dests.count; j++) { - if (variableLabelInstr.dests.orig[j].equals(variable)) { - variableLabelInstr.dests.curr[j] = register; - - // Need to iterate over predecessors and fill jumps as well - for (int k = 0; k < labelBlock.getPredecessorCount(); k++) { - var pred = labelBlock.getPredecessorAt(k); - var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); - jumpOp.alive.curr[j] = register; - } - } - } - - for (var aliasEntry : this.usageAliasMap.entrySet()) { - var originalVariable = aliasEntry.getValue(); - if (!originalVariable.equals(variable)) { - continue; - } - - var aliasVariable = aliasEntry.getKey(); - var aliasLabelInstr = this.labelMap.get(aliasVariable); - if (aliasLabelInstr == null) { - continue; - } - - this.labelMap.remove(aliasVariable); - - var aliasLabelBlock = labelToBlockMap.get(aliasLabelInstr); - for (int j = 0; j < aliasLabelInstr.dests.count; j++) { - if (aliasLabelInstr.dests.orig[j].equals(aliasVariable)) { - aliasLabelInstr.dests.curr[j] = register; - - // Need to iterate over predecessors and fill jumps as well - for (int k = 0; k < aliasLabelBlock.getPredecessorCount(); k++) { - var pred = aliasLabelBlock.getPredecessorAt(k); - var jumpOp = (RAVInstruction.Op) this.blockInstructions.get(pred).getLast(); - jumpOp.alive.curr[j] = register; - } - } - } - } - } - - private void tryMappingVariable( - // @formatter:off - Map> labelToBlockMap, RAValue raVariable, - RAValue raLocation, PathEntry path, RAVInstruction.Base useInstruction - // @formatter:on - ) { - if (!raVariable.isVariable()) { - return; - } - - if (raLocation.isVariable() || raLocation.getValue() instanceof ConstantValue) { - return; - } - - this.mapLabelVariableFromUsage(labelToBlockMap, raVariable.asVariable(), raLocation, path, useInstruction); - } - - /** - * Resolves label variable registers by finding where they are used. - */ - public void resolvePhiFromUsage() { - Queue worklist = new LinkedList<>(); - worklist.add(new PathEntry(this.lir.getControlFlowGraph().getStartBlock())); - - Map> labelToBlockMap = new EconomicHashMap<>(); - - Set visited = new EconomicHashSet<>(); // Ignore already visited combinations. - while (!worklist.isEmpty()) { - var entry = worklist.poll(); - if (visited.contains(entry)) { - continue; - } - visited.add(entry); - - var block = entry.block; - var instructions = this.blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - for (int i = 0; i < labelInstr.dests.count; i++) { - if (labelInstr.dests.curr[i] == null) { - RAVariable variable = labelInstr.dests.orig[i].asVariable(); - this.labelMap.put(variable, labelInstr); - labelToBlockMap.put(labelInstr, block); - } - } - - for (var instruction : instructions) { - switch (instruction) { - case RAVInstruction.ValueMove move -> { - this.tryMappingVariable(labelToBlockMap, move.variableOrConstant, move.location, entry, instruction); - } - case RAVInstruction.Op op -> { - if (instruction.lirInstruction instanceof StandardOp.JumpOp) { - // Always only one successor for this jump op - // Assumption here is, that we resolve aliases with original registers immediately - // so in-case an alias was defined after that happened, it's not resolved and will fail. - var label = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); - for (int i = 0; i < op.alive.count; i++) { - if (!op.alive.orig[i].isVariable()) { - continue; - } - - var variable = op.alive.orig[i].asVariable(); - if (this.labelMap.get(variable) != null) { - this.usageAliasMap.put(variable, label.dests.orig[i].asVariable()); - } - } - - continue; - } - - for (int i = 0; i < op.uses.count; i++) { - this.tryMappingVariable(labelToBlockMap, op.uses.orig[i], op.uses.curr[i], entry, instruction); - } - - for (int i = 0; i < op.alive.count; i++) { - this.tryMappingVariable(labelToBlockMap, op.alive.orig[i], op.alive.curr[i], entry, instruction); - } - } - default -> { - } - } - } - - for (int i = 0; i < block.getSuccessorCount(); i++) { - var succ = block.getSuccessorAt(i); - worklist.add(entry.next(succ)); - } - } - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 46c8f959032a..57241f5869f0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -19,7 +19,7 @@ * Resolve label variable locations based on their first usage, * globally - spanning over all program blocks for every variable. */ -class FromUsageResolverGlobal { +public class FromUsageResolverGlobal { protected LIR lir; protected BlockMap> blockInstructions; @@ -108,15 +108,12 @@ public void resolvePhiFromUsage() { var instructions = blockInstructions.get(block); for (var instruction : instructions.reversed()) { switch (instruction) { - case RAVInstruction.VirtualMove ignored -> { - } - case RAVInstruction.Move move -> handleMove(usage, move.from, move.to); + case RAVInstruction.LocationMove move -> handleMove(usage, move.from, move.to); case RAVInstruction.Op op -> { if (op.lirInstruction instanceof StandardOp.LabelOp) { this.resolveLabel(usage, op, block); continue; } - // TODO: decide how to deal with locations being overwritten. if (firstUsages.containsKey(op)) { var iterator = firstUsages.get(op).iterator(); @@ -203,6 +200,10 @@ protected void initializeUsages() { // No usage found before this jump var succ = block.getSuccessorAt(0); var succLabel = (RAVInstruction.Op) blockInstructions.get(succ).getFirst(); + var alias = succLabel.dests.orig[i].asVariable(); + if (alias.equals(variable)) { + continue; // Loop + } aliasBlockMap.put(variable, block); aliasMap.put(variable, succLabel.dests.orig[i].asVariable()); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java deleted file mode 100644 index cba80e2cc03f..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelConflictResolver.java +++ /dev/null @@ -1,152 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.util.EconomicHashMap; -import jdk.graal.compiler.util.EconomicHashSet; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class LabelConflictResolver implements ConflictResolver { - protected LIR lir; - protected BlockMap> blockInstructions; - - protected Map> blockMap; - protected Map labelMap; - protected Map> rules; - protected Map> expandedRules; - - public LabelConflictResolver() { - this.blockMap = new EconomicHashMap<>(); - this.labelMap = new EconomicHashMap<>(); - this.rules = new EconomicHashMap<>(); - this.expandedRules = new EconomicHashMap<>(); - } - - @Override - public void prepare(LIR lir, BlockMap> blockInstructions) { - this.lir = lir; - this.blockInstructions = blockInstructions; - - this.buildLabelMap(); - this.buildRules(); - this.expandRules(); - } - - protected void buildLabelMap() { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - - for (int i = 0; i < labelInstr.dests.count; i++) { - if (!labelInstr.dests.orig[i].isVariable()) { - continue; - } - - var labelVariable = labelInstr.dests.orig[i]; - labelMap.put(labelVariable.asVariable(), labelInstr); - blockMap.put(labelVariable.asVariable(), block); - } - } - } - - protected void buildRules() { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - - for (int i = 0; i < labelInstr.dests.count; i++) { - if (!labelInstr.dests.orig[i].isVariable()) { - continue; - } - - var labelVariable = labelInstr.dests.orig[i].asVariable(); - Set resolutions = new EconomicHashSet<>(); - - for (int j = 0; j < block.getPredecessorCount(); j++) { - var pred = block.getPredecessorAt(j); - var predInstructions = blockInstructions.get(pred); - var jumpInstr = (RAVInstruction.Op) predInstructions.getLast(); - - resolutions.add(jumpInstr.alive.orig[i]); - } - - rules.put(labelVariable, resolutions); - } - } - } - - protected void expandRules() { - for (var entry : rules.entrySet()) { - var variable = entry.getKey(); - - ArrayList sources = new ArrayList<>(entry.getValue()); - int sourceCount = sources.size(); - for (int i = 0; i < sourceCount; i++) { - var source = sources.get(i); - if (!source.isVariable()) { - continue; - } - - var sourceVariable = source.asVariable(); - if (rules.containsKey(sourceVariable)) { - for (var newSource : rules.get(sourceVariable)) { - if (sources.contains(newSource)) { - continue; - } - - sources.add(newSource); - sourceCount++; - } - } - } - - expandedRules.put(variable, new EconomicHashSet<>(sources)); - } - } - - protected boolean isSubsetOfValues(Set superset, Set subset) { - for (var v : subset) { - if (!superset.contains(v.getRAValue())) { - return false; - } - } - return true; - } - - @Override - public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { - if (!this.expandedRules.containsKey(target)) { - return null; - } - - var ruleSet = this.expandedRules.get(target); - var confStates = conflictedState.getConflictedStates(); - - if (!this.isSubsetOfValues(ruleSet, confStates)) { - return null; - } - - return new ValueAllocationState(target, labelMap.get(target), blockMap.get(target)); - } - - @Override - public ValueAllocationState resolveValueState(RAVariable target, ValueAllocationState valueState, RAValue location) { - if (!this.expandedRules.containsKey(target)) { - return null; - } - - var ruleSet = this.expandedRules.get(target); - if (!ruleSet.contains(valueState.getRAValue())) { - return null; - } - - return new ValueAllocationState(target, labelMap.get(target), blockMap.get(target)); - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java deleted file mode 100644 index 635c95083690..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedConstantFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.type.DataPointerConstant; -import jdk.graal.compiler.debug.GraalError; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.JavaConstant; - -/** - * Factory to create a tagged constant from an existing constant. - */ -public class TaggedConstantFactory { - protected int lastTag; - - public TaggedConstantFactory(int initialTag) { - this.lastTag = initialTag; - } - - public TaggedConstantFactory() { - this(0); - } - - public Constant createNew(Constant constant) { - return switch (constant) { - case JavaConstant javaConst -> this.createNew(javaConst); - case DataPointerConstant ptrConst -> this.createNew(ptrConst); - default -> throw new GraalError("Unhandled constant type"); - }; - } - - public Constant createNew(JavaConstant constant) { - return new TaggedJavaConstant(constant, this.lastTag++); - } - - public Constant createNew(DataPointerConstant constant) { - return new TaggedDataPointerConstant(constant, this.lastTag++); - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java deleted file mode 100644 index 55455a63a205..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedDataPointerConstant.java +++ /dev/null @@ -1,58 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.type.DataPointerConstant; - -import java.nio.ByteBuffer; - -/** - * Data pointer constant acting as a variable (based on its tag) - * to make resolving from jump it easier. - */ -public class TaggedDataPointerConstant extends DataPointerConstant { - protected DataPointerConstant constant; - protected int tag; - - protected TaggedDataPointerConstant(DataPointerConstant constant, int tag) { - super(constant.getAlignment()); - this.constant = constant; - this.tag = tag; - } - - @Override - public int getSerializedSize() { - return this.constant.getSerializedSize(); - } - - @Override - public void serialize(ByteBuffer buffer) { - this.constant.serialize(buffer); - } - - @Override - public String toValueString() { - return this.constant.toValueString(); - } - - @Override - public int hashCode() { - return this.tag ^ constant.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o instanceof TaggedDataPointerConstant otherTagged) { - return otherTagged.tag == tag && this.constant.equals(otherTagged.constant); - } - - return constant.equals(o); - } - - @Override - public String toString() { - return "t" + this.tag + ":" + constant.toString(); - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java deleted file mode 100644 index a480e346dde0..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TaggedJavaConstant.java +++ /dev/null @@ -1,91 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; - -/** - * Java constant acting as a variable (based on its tag) to - * make resolving from jump it easier. - */ -public class TaggedJavaConstant implements JavaConstant { - protected JavaConstant constant; - protected int tag; - - protected TaggedJavaConstant(JavaConstant constant, int tag) { - this.constant = constant; - this.tag = tag; - } - - @Override - public int hashCode() { - return this.tag ^ constant.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o instanceof TaggedJavaConstant otherTagged) { - return otherTagged.tag == tag && this.constant.equals(otherTagged.constant); - } - - return constant.equals(o); - } - - @Override - public String toString() { - return "t" + this.tag + ":" + constant.toString(); - } - - @Override - public JavaKind getJavaKind() { - return this.constant.getJavaKind(); - } - - @Override - public boolean isNull() { - return this.constant.isNull(); - } - - @Override - public boolean isDefaultForKind() { - return this.constant.isDefaultForKind(); - } - - @Override - public Object asBoxedPrimitive() { - return this.constant.asBoxedPrimitive(); - } - - @Override - public int asInt() { - return this.constant.asInt(); - } - - @Override - public boolean asBoolean() { - return this.constant.asBoolean(); - } - - @Override - public long asLong() { - return this.constant.asLong(); - } - - @Override - public float asFloat() { - return this.constant.asFloat(); - } - - @Override - public double asDouble() { - return this.constant.asDouble(); - } - - @Override - public String toValueString() { - return this.constant.toValueString(); - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java deleted file mode 100644 index bc4a34dac2ea..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/TargetLocationOverwrittenException.java +++ /dev/null @@ -1,23 +0,0 @@ -package jdk.graal.compiler.lir.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; - -/** - * Target location was overwritten when looking for - * label variable locations by their usage. - */ -@SuppressWarnings("serial") -public class TargetLocationOverwrittenException extends RAVException { - public RAVInstruction.ValueMove valueMove; - public BasicBlock block; - - public TargetLocationOverwrittenException(RAVInstruction.ValueMove move, BasicBlock block) { - super(getErrorMessage(move, block)); - this.valueMove = move; - this.block = block; - } - - static String getErrorMessage(RAVInstruction.ValueMove move, BasicBlock block) { - return "Target location " + move.location + " was overwritten by " + move.lirInstruction + " in " + block; - } -} From b061de7593429e7d32ab51ac750f37be56173ebc Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 21 Feb 2026 19:01:49 +0100 Subject: [PATCH 053/112] Add license header --- .../AliveConstraintViolationException.java | 26 +++++++++++++- .../lir/alloc/verifier/AllocationState.java | 24 +++++++++++++ .../lir/alloc/verifier/ConflictResolver.java | 24 +++++++++++++ .../verifier/ConflictedAllocationState.java | 24 +++++++++++++ ...nstantMaterializationConflictResolver.java | 27 +++++++++++++-- .../verifier/FromUsageResolverGlobal.java | 24 +++++++++++++ .../InvalidRegisterUsedException.java | 24 +++++++++++++ .../verifier/KindsMismatchException.java | 26 +++++++++++++- .../alloc/verifier/LabelNotResolvedError.java | 24 +++++++++++++ .../verifier/MergedAllocationStateMap.java | 24 +++++++++++++ .../verifier/MergedBlockVerifierState.java | 27 +++++++++++++-- .../alloc/verifier/MissingLocationError.java | 26 +++++++++++++- .../compiler/lir/alloc/verifier/RAVError.java | 24 +++++++++++++ .../lir/alloc/verifier/RAVException.java | 24 +++++++++++++ .../RAVFailedVerificationException.java | 24 +++++++++++++ .../lir/alloc/verifier/RAVInstruction.java | 34 ++++++++++++++++--- .../compiler/lir/alloc/verifier/RAValue.java | 24 +++++++++++++ .../lir/alloc/verifier/RAVariable.java | 24 +++++++++++++ .../verifier/RegisterAllocationVerifier.java | 24 +++++++++++++ .../RegisterAllocationVerifierPhase.java | 28 +++++++++++++-- .../RegisterAllocationVerifierPhaseState.java | 31 +++++++++++++++-- ...aterializedConstantSourceMissingError.java | 24 +++++++++++++ .../verifier/UnknownAllocationState.java | 24 +++++++++++++ .../verifier/UnknownInstructionError.java | 24 +++++++++++++ .../alloc/verifier/ValueAllocationState.java | 24 +++++++++++++ .../verifier/ValueNotInRegisterException.java | 26 +++++++++++++- .../lir/alloc/verifier/VerifierPrinter.java | 24 +++++++++++++ 27 files changed, 665 insertions(+), 18 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index 0161a7f2d8e1..b6a29356506c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; @@ -14,7 +38,7 @@ public class AliveConstraintViolationException extends RAVException { public BasicBlock block; /** - * Construct an AliveConstraintViolationException + * Construct an AliveConstraintViolationException. * * @param instruction Instruction where violation occurred * @param block Block where violation occurred diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index 3011bcdab5ce..c1a5c979a467 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index 0ed5a8c5d23b..54a8801c43e6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BlockMap; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index a0c9a6583494..52f843ada866 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.util.EconomicHashSet; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 276555d667da..c1032a216211 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -1,14 +1,37 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.code.StackSlot; import java.util.List; import java.util.Map; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 57241f5869f0..9940d1ba5b55 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java index 8910cb05fd69..7ce4bd28525f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.vm.ci.code.Register; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java index cbbe15818913..121c5ed0aeb1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; @@ -15,7 +39,7 @@ public class KindsMismatchException extends RAVException { public boolean origVsCurr; /** - * Construct a KindsMismatchException + * Construct a KindsMismatchException. * * @param instruction Instruction where violation occurred * @param block Block where violation occurred diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java index 575386975aa6..b71ee43bf725 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index ca8cd417f571..483be711c9aa 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index bc512ab34820..2e7189f012b9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.LIRKindWithCast; @@ -8,7 +32,6 @@ import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.StandardOp; import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ValueKind; /** @@ -341,7 +364,7 @@ public void update(RAVInstruction.Base instruction, BasicBlock block) { } /** - * Update the state using a generic operation + * Update the state using a generic operation. * * @param op Operation we update state from * @param block Block where it is located diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java index eda397b92b88..89c1df939087 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; @@ -9,7 +33,7 @@ @SuppressWarnings("serial") public class MissingLocationError extends RAVError { /** - * Construct a MissingLocationError + * Construct a MissingLocationError. * * @param instruction Instruction where violation occurred * @param block Block where violation occurred diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java index 61e006afe915..dcaeb2eeb57c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.debug.GraalError; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java index 37dfc88fdbe4..998922734a3d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.debug.GraalError; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java index 24577d2e4346..9766a532c406 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import java.util.List; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 96f496880cc2..74d6a71e3ab3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.lir.InstructionValueProcedure; @@ -14,7 +38,7 @@ public class RAVInstruction { /** - * Base class for all RAV instructions + * Base class for all RAV instructions. */ public abstract static class Base { /** @@ -187,11 +211,11 @@ public String toString() { */ public static class Op extends Base { /** - * Pairs of outputs generated by this operation + * Pairs of outputs generated by this operation. */ public ValueArrayPair dests; /** - * Pairs of inputs used by this operation + * Pairs of inputs used by this operation. */ public ValueArrayPair uses; /** @@ -295,7 +319,7 @@ public String toString() { } /** - * Move between two registers + * Move between two registers. */ public static class RegMove extends LocationMove { public RAValue from; @@ -381,7 +405,7 @@ public static class ValueMove extends Base { public RAValue variableOrConstant; /** - * Where variable (or constant) is being stored + * Where variable (or constant) is being stored. */ public RAValue location; // Can also be another variable! diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index 953f943a90d0..62976ceca1ea 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.lir.LIRValueUtil; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java index a59d946ba2cd..d671e10c9baa 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.lir.Variable; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 8ce34d5bce03..731e104fa1ba 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index de1a71c11627..a1abf3f7c3a7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; @@ -28,7 +52,7 @@ import java.util.Set; /** - * Verification phase for Register Allocation + * Verification phase for Register Allocation. */ public class RegisterAllocationVerifierPhase extends AllocationPhase { public static class Options { @@ -46,7 +70,7 @@ public static class Options { } /** - * Shared phase state + * Shared phase state. */ private final RegisterAllocationVerifierPhaseState state; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java index 085435b0dcc2..8cfd206489af 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -1,11 +1,36 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.options.OptionValues; +import jdk.graal.compiler.util.EconomicHashMap; +import org.graalvm.collections.Equivalence; -import java.util.IdentityHashMap; import java.util.Map; /** @@ -23,7 +48,7 @@ public class RegisterAllocationVerifierPhaseState { protected Map> verifierInstructions; public RegisterAllocationVerifierPhaseState(OptionValues options) { - this.verifierInstructions = new IdentityHashMap<>(); + this.verifierInstructions = new EconomicHashMap<>(Equivalence.IDENTITY); this.phiResolution = RegisterAllocationVerifierPhase.Options.RAPhiResolution.getValue(options); this.moveConstants = RegisterAllocationVerifierPhase.Options.MoveConstants.getValue(options); @@ -52,7 +77,7 @@ public boolean shouldBeVerified(LIRGenerationResult lirGenRes) { * @return New instruction map */ public Map createInstructionMap(LIRGenerationResult lirGenRes) { - var idMap = new IdentityHashMap(); + Map idMap = new EconomicHashMap<>(Equivalence.IDENTITY); this.verifierInstructions.put(lirGenRes, idMap); return idMap; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java index 25e4842e9528..7977599345fa 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index 07d2fef8ce67..327b511bfd29 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java index f42c758dc5d5..2cb7867b78d7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index ead3fb3a0b88..a0248d90866a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 3999f631fd60..2c86627c11be 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; @@ -15,7 +39,7 @@ public class ValueNotInRegisterException extends RAVException { public AllocationState state; /** - * Construct a ValueNotInRegisterException + * Construct a ValueNotInRegisterException. * * @param instruction Instruction where violation occurred * @param block Block where violation occurred diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index f0a0868ac93c..95667d7f79d7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BlockMap; From 24afdde790d85dab8abe3b5c902f4ca438692f68 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 23 Feb 2026 09:36:15 +0100 Subject: [PATCH 054/112] Switch instaceof for ValueTool(s) --- ...nstantMaterializationConflictResolver.java | 10 +++--- .../verifier/MergedBlockVerifierState.java | 12 ++++--- .../verifier/PreRegisterAllocationPhase.java | 4 +-- .../compiler/lir/alloc/verifier/RAValue.java | 13 +++++--- .../RegisterAllocationVerifierPhase.java | 31 +++++++------------ .../alloc/verifier/ValueAllocationState.java | 7 ++--- 6 files changed, 36 insertions(+), 41 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index c1032a216211..dbcf8c399a1d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -118,7 +118,7 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted } variable = value.asVariable(); - } else if (value.getValue() instanceof ConstantValue) { + } else if (LIRValueUtil.isConstantValue(value.getValue())) { if (constantState != null && !constantState.getRAValue().equals(value)) { return null; } @@ -160,8 +160,10 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati return null; } - if (valueState.getRAValue().getValue() instanceof ConstantValue constant) { - if (!this.constantVariableMap.get(variable).equals(constant)) { + var stateValue = valueState.getRAValue().getValue(); + if (LIRValueUtil.isConstantValue(stateValue)) { + var constantValue = LIRValueUtil.asConstantValue(stateValue); + if (!this.constantVariableMap.get(variable).equals(constantValue)) { return null; } @@ -196,7 +198,7 @@ protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllo var source = state.getSource(); if (source instanceof RAVInstruction.ValueMove move) { var location = move.location.getValue(); - return location instanceof StackSlot || location instanceof VirtualStackSlot; + return LIRValueUtil.isStackSlotValue(location); } else { throw new RematerializedConstantSourceMissingError(source, variable); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 2e7189f012b9..aace73a469a7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -30,8 +30,10 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.CastValue; import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.ValueKind; /** @@ -163,11 +165,11 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } if (!valAllocState.value.equals(orig)) { - if (orig.getValue() instanceof CastValue castValue && valAllocState.value.getValue().equals(castValue.underlyingValue())) { + if (LIRValueUtil.isCast(orig.getValue()) && valAllocState.value.getValue().equals(LIRValueUtil.uncast(orig.getValue()))) { continue; // They aren't equal here because of the CastValue, so if they are equal afterwards, we skip next part. } - if (valAllocState.value.getValue() instanceof ConstantValue) { + if (LIRValueUtil.isConstantValue(valAllocState.value.getValue())) { var variable = orig.asVariable(); var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); if (resolvedState != null) { @@ -231,8 +233,8 @@ protected boolean kindsEqual(RAValue orig, RAValue curr) { protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { ValueKind origKind = orig.getValue().getValueKind(); ValueKind currKind = fromState.getValue().getValueKind(); - if (orig.getValue() instanceof CastValue castOrig) { - origKind = castOrig.underlyingValue().getValueKind(); + if (LIRValueUtil.isCast(orig.getValue())) { + origKind = LIRValueUtil.uncast(orig.getValue()).getValueKind(); } return origKind.equals(currKind); @@ -416,7 +418,7 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { * @param block Block where it is located */ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove, BasicBlock block) { - if (valueMove.location.getValue() instanceof RegisterValue) { + if (ValueUtil.isRegister(valueMove.location.getValue())) { this.values.put(valueMove.location, new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); } else if (valueMove.location.isVariable()) { // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 1731251a2daa..9e61cece5c2c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -239,7 +239,7 @@ protected boolean isVirtualMove(LIRInstruction instruction) { var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); var input = valueMov.getInput(); - return (input instanceof RegisterValue || input instanceof StackSlot /*|| input instanceof AbstractAddress*/) && LIRValueUtil.isVariable(valueMov.getResult()); + return (ValueUtil.isRegister(input) || LIRValueUtil.isStackSlotValue(input)) && LIRValueUtil.isVariable(valueMov.getResult()); } /** @@ -260,6 +260,6 @@ protected boolean isSpeculativeMove(LIRInstruction instruction) { var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); var result = valueMov.getResult(); // Result could be variable or register - return (result instanceof RegisterValue || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); + return (ValueUtil.isRegister(result) || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index 62976ceca1ea..1dd5509a1d53 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -26,6 +26,7 @@ import jdk.graal.compiler.lir.LIRValueUtil; import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; /** @@ -77,8 +78,8 @@ public boolean isVariable() { @Override public int hashCode() { - if (this.value instanceof RegisterValue registerValue) { - return registerValue.getRegister().hashCode(); + if (ValueUtil.isRegister(this.value)) { + return ValueUtil.asRegisterValue(this.value).getRegister().hashCode(); } return this.value.hashCode(); @@ -94,7 +95,9 @@ public int hashCode() { @Override public boolean equals(Object other) { if (other instanceof RAValue otherValueWrap) { - if (this.value instanceof RegisterValue thisReg && otherValueWrap.value instanceof RegisterValue otherReg) { + if (ValueUtil.isRegister(this.value) && ValueUtil.isRegister(otherValueWrap.value)) { + var thisReg = ValueUtil.asRegisterValue(this.value); + var otherReg = ValueUtil.asRegisterValue(otherValueWrap.value); return thisReg.getRegister().equals(otherReg.getRegister()); } @@ -106,8 +109,8 @@ public boolean equals(Object other) { @Override public String toString() { - if (value instanceof RegisterValue regValue) { - return regValue.getRegister().toString(); + if (ValueUtil.isRegister(this.value)) { + return ValueUtil.asRegisterValue(this.value).getRegister().toString(); } return value.toString(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index a1abf3f7c3a7..294952527831 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -29,6 +29,7 @@ import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; @@ -42,6 +43,8 @@ import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.Value; import java.io.FileNotFoundException; import java.io.PrintStream; @@ -236,26 +239,14 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) var input = valueMov.getInput(); var result = valueMov.getResult(); - if (input instanceof VirtualStackSlot stackSlot && result instanceof RegisterValue reg) { - return new RAVInstruction.Reload(instruction, reg, stackSlot); - } else if (result instanceof VirtualStackSlot stackSlot && input instanceof RegisterValue reg) { - return new RAVInstruction.Spill(instruction, stackSlot, reg); - } else if (input instanceof RegisterValue reg1 && result instanceof RegisterValue reg2) { - return new RAVInstruction.RegMove(instruction, reg1, reg2); - } else if (input instanceof StackSlot stackSlot && result instanceof RegisterValue reg) { - return new RAVInstruction.Reload(instruction, reg, stackSlot); - } else if (input instanceof RegisterValue reg && result instanceof StackSlot stackSlot) { - return new RAVInstruction.Spill(instruction, stackSlot, reg); - } - - if (input instanceof StackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof StackSlot stackSlot1 && result instanceof VirtualStackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); - } else if (input instanceof VirtualStackSlot stackSlot1 && result instanceof StackSlot stackSlot2) { - return new RAVInstruction.StackMove(instruction, stackSlot1, stackSlot2); + if (LIRValueUtil.isStackSlotValue(input) && ValueUtil.isRegister(result)) { + return new RAVInstruction.Reload(instruction, ValueUtil.asRegisterValue(result), input); + } else if (LIRValueUtil.isStackSlotValue(result) && ValueUtil.isRegister(input)) { + return new RAVInstruction.Spill(instruction, result, ValueUtil.asRegisterValue(input)); + } else if (ValueUtil.isRegister(input) && ValueUtil.isRegister(result)) { + return new RAVInstruction.RegMove(instruction, ValueUtil.asRegisterValue(input), ValueUtil.asRegisterValue(result)); + } else if (LIRValueUtil.isStackSlotValue(input) && LIRValueUtil.isStackSlotValue(result)) { + return new RAVInstruction.StackMove(instruction, input, result); } return null; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index a0248d90866a..2e0b1809f49d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -26,11 +26,8 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.VirtualStackSlot; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; /** @@ -44,7 +41,7 @@ public class ValueAllocationState extends AllocationState implements Cloneable { public ValueAllocationState(RAValue raValue, RAVInstruction.Base source, BasicBlock block) { var value = raValue.getValue(); - if (value instanceof RegisterValue || LIRValueUtil.isVariable(value) || value instanceof ConstantValue || value instanceof StackSlot || value instanceof VirtualStackSlot || Value.ILLEGAL.equals(value)) { + if (ValueUtil.isRegister(value) || LIRValueUtil.isVariable(value) || LIRValueUtil.isConstantValue(value) || LIRValueUtil.isStackSlotValue(value) || Value.ILLEGAL.equals(value)) { // Here, we make sure that no new value class is used here, without consideration. // StackSlot, RegisterValue is present in start block in label as predefined argument From c7e252862919d2fd492cda4f60be09749beefb69 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 2 Mar 2026 22:54:02 +0100 Subject: [PATCH 055/112] Remove old code and do refactoring --- .../lir/alloc/verifier/AllocationState.java | 13 +- .../verifier/ConflictedAllocationState.java | 29 +- .../verifier/FromUsageResolverGlobal.java | 6 +- .../verifier/MergedAllocationStateMap.java | 83 +---- .../verifier/MergedBlockVerifierState.java | 325 ++++++++++++------ .../OperandFlagMismatchException.java | 56 +++ .../lir/alloc/verifier/PhiResolution.java | 46 +-- .../verifier/PreRegisterAllocationPhase.java | 131 ++----- .../lir/alloc/verifier/RARegister.java | 82 +++++ .../lir/alloc/verifier/RAVException.java | 4 + .../lir/alloc/verifier/RAVInstruction.java | 47 ++- .../compiler/lir/alloc/verifier/RAValue.java | 32 +- .../verifier/RegisterAllocationVerifier.java | 140 +------- .../RegisterAllocationVerifierPhaseState.java | 2 - .../verifier/UnknownAllocationState.java | 8 +- .../alloc/verifier/ValueAllocationState.java | 9 +- .../lir/alloc/verifier/VerifierPrinter.java | 3 + 17 files changed, 535 insertions(+), 481 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index c1a5c979a467..eba27c1e4d6f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -24,15 +24,18 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; + /** * Interface for AllocationState stored in AllocationStateMap, * describing what state physical location is in. */ public abstract class AllocationState { /** - * No location is ever just null, always at least Unknown. + * Get the default allocation state for every location, + * instead of null, we have Unknown state. * - * @return + * @return Default state for every location */ public static AllocationState getDefault() { return UnknownAllocationState.INSTANCE; @@ -69,10 +72,12 @@ public boolean isConflicted() { * the program graph, decide what result of said two states * should be. * - * @param other Other state coming from a predecessor edge + * @param other Other state coming from a predecessor edge + * @param otherBlock Which block is other state from + * @param block Which state is this state from * @return What is the new state the location is in. */ - public abstract AllocationState meet(AllocationState other); + public abstract AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block); public abstract boolean equals(AllocationState other); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 52f843ada866..0cc0165195df 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.util.EconomicHashSet; import java.util.Arrays; @@ -44,18 +45,32 @@ public ConflictedAllocationState() { public ConflictedAllocationState(ValueAllocationState state1, ValueAllocationState state2) { this(); - this.conflictedStates.add(state1); // Not using addConflictedValue because a warning is thrown - this.conflictedStates.add(state2); + addConflictedValue(state1); + addConflictedValue(state2); } protected ConflictedAllocationState(Set conflictedStates) { this.conflictedStates = new EconomicHashSet<>(conflictedStates); } - public void addConflictedValue(ValueAllocationState state) { + public final void addConflictedValue(ValueAllocationState state) { + if (hasConflictedValue(state)) { + return; + } + this.conflictedStates.add(state); } + public final boolean hasConflictedValue(ValueAllocationState valueAllocationState) { + for (var state : this.conflictedStates) { + if (state.getRAValue().equals(valueAllocationState.getRAValue())) { + return true; + } + } + + return false; + } + /** * Get the set of all ValueAllocationState instances conflicting in this state. * @@ -78,14 +93,16 @@ public boolean isConflicted() { * @return ConflictedAllocationState with predecessor state added up */ @Override - public AllocationState meet(AllocationState other) { + public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { var newlyConflictedState = new ConflictedAllocationState(this.getConflictedStates()); if (other instanceof ValueAllocationState valueState) { newlyConflictedState.addConflictedValue(valueState); } if (other instanceof ConflictedAllocationState conflictedState) { - newlyConflictedState.conflictedStates.addAll(conflictedState.conflictedStates); + for (var otherConfState : conflictedState.getConflictedStates()) { + newlyConflictedState.addConflictedValue(otherConfState); + } } if (other instanceof UnknownAllocationState) { @@ -94,7 +111,7 @@ public AllocationState meet(AllocationState other) { // and it means that this location was not defined there, but it was defined in a // different predecessor block, meaning it's now in a conflicted state, where // it either is defined or it is not - should not be used in further blocks. - newlyConflictedState.conflictedStates.add(ValueAllocationState.createIllegal()); + newlyConflictedState.addConflictedValue(ValueAllocationState.createIllegal(otherBlock)); } return newlyConflictedState; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 9940d1ba5b55..3458ea1d8baf 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -32,8 +32,8 @@ import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; @@ -116,7 +116,7 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b * Resolves label variable registers by finding where they are used. */ public void resolvePhiFromUsage() { - Queue> worklist = new LinkedList<>(); + Queue> worklist = new ArrayDeque<>(); this.initializeUsages(); @@ -175,7 +175,7 @@ public void resolvePhiFromUsage() { } protected void initializeUsages() { - Queue> worklist = new LinkedList<>(); + Queue> worklist = new ArrayDeque<>(); var startBlock = this.lir.getControlFlowGraph().getStartBlock(); worklist.add(startBlock); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java index 483be711c9aa..949d1a0da150 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java @@ -25,9 +25,9 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.code.ValueUtil; import java.util.Map; import java.util.Set; @@ -36,29 +36,12 @@ * Mapping between a location and it's AllocationState. */ public class MergedAllocationStateMap { + protected BasicBlock block; + /** * Internal map maintaining the mapping. */ protected Map internalMap; - /** - * Integer describing when was the value inserted, - * the bigger it is, the newer the value is. - */ - protected Map locationTimings; - /** - * Prioritized locations are ones made by the register allocator itself. - *

- * Whenever we are resolving phi variables, these are prioritized - * because they are likely what the register allocator chose - * to be used as phi locations. - *

- * If there's multiple, then time should make the difference. - */ - protected Map prioritizedLocations; - /** - * Internal constant for locationTimings. - */ - protected int time; /** * Register allocation config describing which registers @@ -66,24 +49,22 @@ public class MergedAllocationStateMap { */ protected RegisterAllocationConfig registerAllocationConfig; - public MergedAllocationStateMap(RegisterAllocationConfig registerAllocationConfig) { + public MergedAllocationStateMap(BasicBlock block, RegisterAllocationConfig registerAllocationConfig) { internalMap = new EconomicHashMap<>(); - locationTimings = new EconomicHashMap<>(); - prioritizedLocations = new EconomicHashMap<>(); - time = 0; - + this.block = block; this.registerAllocationConfig = registerAllocationConfig; } - public MergedAllocationStateMap(MergedAllocationStateMap other) { + public MergedAllocationStateMap(BasicBlock block, MergedAllocationStateMap other) { internalMap = new EconomicHashMap<>(other.internalMap); - locationTimings = new EconomicHashMap<>(other.locationTimings); - prioritizedLocations = new EconomicHashMap<>(other.prioritizedLocations); - time = other.time + 1; - + this.block = block; registerAllocationConfig = other.registerAllocationConfig; } + public boolean has(RAValue key) { + return internalMap.containsKey(key); + } + public AllocationState get(RAValue key) { return this.get(key, AllocationState.getDefault()); } @@ -96,11 +77,6 @@ public AllocationState get(RAValue key, AllocationState defaultValue) { return state; } - public void putAsPrioritized(RAValue key, AllocationState value) { - this.put(key, value); - this.prioritizedLocations.put(key, true); - } - /** * Put a new state for location to the map, * while checking if register can be allocated to. @@ -126,12 +102,7 @@ public void put(RAValue key, AllocationState state) { * @param state State to store */ public void putWithoutRegCheck(RAValue key, AllocationState state) { - locationTimings.put(key, time++); internalMap.put(key, state); - - if (prioritizedLocations.containsKey(key)) { - prioritizedLocations.put(key, false); - } } /** @@ -149,27 +120,6 @@ public void putClone(RAValue key, AllocationState state) { this.put(key, state.clone()); } - public void putCloneAsPrioritized(RAValue key, AllocationState value) { - this.putClone(key, value); - this.prioritizedLocations.put(key, true); - } - - public boolean isPrioritized(RAValue key) { - return prioritizedLocations.containsKey(key); - } - - /** - * Get time when the location was inserted. - * - * @param key Location used - * @return integer describing the insertion time - bigger = newer. - */ - public int getKeyTime(RAValue key) { - var time = locationTimings.get(key); - assert time != null : "Time for key " + key + " not present."; - return time; - } - /** * Get set of locations holding this particular variable/constant. * @@ -205,7 +155,7 @@ public boolean mergeWith(MergedAllocationStateMap source) { } var currentValue = this.internalMap.get(entry.getKey()); - var result = this.internalMap.get(entry.getKey()).meet(entry.getValue()); + var result = this.internalMap.get(entry.getKey()).meet(entry.getValue(), source.block, this.block); if (!currentValue.equals(result)) { changed = true; } @@ -220,16 +170,15 @@ public boolean mergeWith(MergedAllocationStateMap source) { * Check if register can be used by the register allocator. * If not allowed, an exception is thrown. * - * @param raLocation Value that could be a register. + * @param location Value that could be a register. */ - protected void checkRegisterDestinationValidity(RAValue raLocation) { - var location = raLocation.getValue(); - if (!ValueUtil.isRegister(location)) { + protected void checkRegisterDestinationValidity(RAValue location) { + if (!location.isRegister()) { return; } // Equality check so we know that this change was made by the register allocator. - var register = ValueUtil.asRegister(location); + var register = location.asRegister().getRegister(); if (!this.registerAllocationConfig.getAllocatableRegisters().contains(register)) { throw new InvalidRegisterUsedException(register); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index aace73a469a7..b88e37f7fb7c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -24,18 +24,21 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.LIRKindWithCast; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.CastValue; -import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; +import java.util.ArrayList; +import java.util.List; + /** * Verification state a block is in. */ @@ -45,35 +48,34 @@ public class MergedBlockVerifierState { */ public MergedAllocationStateMap values; + /** + * Label variable resolution method used. + */ protected PhiResolution phiResolution; + + /** + * Register allocation config we use to check if only + * allocatable registers are the ones used. + */ protected RegisterAllocationConfig registerAllocationConfig; /** * Conflict resolver for constant materialization. */ protected ConflictResolver conflictConstantResolver; - protected ConflictResolver labelConflictResolver; - public MergedBlockVerifierState(RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { - this.values = new MergedAllocationStateMap(registerAllocationConfig); + public MergedBlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver) { + this.values = new MergedAllocationStateMap(block, registerAllocationConfig); this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; - this.labelConflictResolver = labelConflictResolver; } - protected MergedBlockVerifierState(MergedBlockVerifierState other, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver, ConflictResolver labelConflictResolver) { - this.phiResolution = phiResolution; - this.registerAllocationConfig = registerAllocationConfig; - this.conflictConstantResolver = constantConflictResolver; - this.labelConflictResolver = labelConflictResolver; - - if (other == null) { - this.values = new MergedAllocationStateMap(registerAllocationConfig); - return; - } - - this.values = new MergedAllocationStateMap(other.values); + protected MergedBlockVerifierState(BasicBlock block, MergedBlockVerifierState other) { + this.phiResolution = other.phiResolution; + this.registerAllocationConfig = other.registerAllocationConfig; + this.conflictConstantResolver = other.conflictConstantResolver; + this.values = new MergedAllocationStateMap(block, other.values); } public MergedAllocationStateMap getValues() { @@ -107,24 +109,31 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. assert orig != null; - boolean isJump = op.lirInstruction instanceof StandardOp.JumpOp; if (curr == null) { - if (isJump) { - if (phiResolution == PhiResolution.FromUsage || phiResolution == PhiResolution.FromUsageGlobal) { + if (op.isJump()) { + if (phiResolution == PhiResolution.ByAllocator || phiResolution == PhiResolution.FromUsageGlobal) { // Variable has no usage, thus no location present. continue; } - if (phiResolution == PhiResolution.ByAllocator || phiResolution == PhiResolution.FromConflicts) { - continue; - } - throw new LabelNotResolvedError(block, labelOp, phiResolution); } throw new MissingLocationError(op.lirInstruction, block, orig); } + if (!kindsEqual(orig, curr)) { + if (!op.isJump()) { + throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); + } + + // Skip when jump due to this case: + // "rdx|QWORD[*] = MOVE input: rdx|QWORD[.+] moveKind: QWORD" + // this move is inserted by the allocator and changes type + // of rdx from [.+] (compressed reference) (same as original variable) to [*] (invalid reference) + } + + AllocationState state = this.values.get(curr); if (orig.equals(curr)) { // For these cases we do not consider checking state taking the original // register as a symbol, because there's too many cases when this does @@ -135,28 +144,21 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. continue; } - if (!kindsEqual(orig, curr) && !isJump) { - throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); - } - - AllocationState state = this.values.get(curr); if (state.isUnknown()) { throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } if (state.isConflicted()) { - var variable = orig.asVariable(); - var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); - - if (phiResolution == PhiResolution.FromConflicts && resolvedState == null) { - resolvedState = this.labelConflictResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); - if (resolvedState == null) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + if (orig.isVariable()) { + var variable = orig.asVariable(); + var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); + if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { + this.values.put(curr, resolvedState); + continue; } } - this.values.put(curr, resolvedState); - continue; + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } if (state instanceof ValueAllocationState valAllocState) { @@ -172,16 +174,7 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. if (LIRValueUtil.isConstantValue(valAllocState.value.getValue())) { var variable = orig.asVariable(); var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); - if (resolvedState != null) { - this.values.put(curr, resolvedState); - continue; - } - } - - if (phiResolution == PhiResolution.FromConflicts && orig.isVariable()) { - var variable = orig.asVariable(); - var resolvedState = labelConflictResolver.resolveValueState(variable, valAllocState, curr); - if (resolvedState != null) { + if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { this.values.put(curr, resolvedState); continue; } @@ -240,6 +233,52 @@ protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { return origKind.equals(currKind); } + /** + * Check that all instruction arrays of pairs of original variable and current location + * check out to the state stored in for this block. + * + * @param instruction Instruction we are checking + * @param block Block where it is located + * @param labelOp Label of the successor block, in-case resolution failed + */ + public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstruction.Op labelOp) { + if (instruction instanceof RAVInstruction.Op op) { + checkInputs(op.uses, op, block, labelOp); + checkInputs(op.alive, op, block, labelOp); + checkInputs(op.stateValues, op, block, labelOp); + + checkTempKind(op, block); + checkAliveConstraint(op, block); + checkStateJavaKinds(op, block); + + checkOperandFlags(op.dests, op, block); + checkOperandFlags(op.uses, op, block); + checkOperandFlags(op.alive, op, block); + checkOperandFlags(op.temp, op, block); + } + } + + /** + * Check if kinds in the temporary array match before allocation + * original variables with after allocation concrete locations. + * + * @param op Instruction we update state from + * @param block Block where it is located + * @throws KindsMismatchException if a pair does not match + */ + protected void checkTempKind(RAVInstruction.Op op, BasicBlock block) { + for (int i = 0; i < op.temp.count; i++) { + var curr = op.temp.curr[i]; + var orig = op.temp.orig[i]; + + if (!kindsEqual(orig, curr)) { + // Make sure the assigned register has the correct kind for temp. + throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); + } + } + } + + /** * Check if alive constraint is not being violated, * when one location is supposed to be alive after instruction @@ -274,79 +313,90 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock } /** - * Check that all instruction arrays of pairs of original variable and current location - * check out to the state stored in for this block. + * Checks values from frame state to see if ones marked as Object JavaKind, + * are a reference in LIRKind. * - * @param instruction Instruction we are checking - * @param block Block where it is located - * @param labelOp Label of the successor block, in-case resolution failed + * @param op Operation which we are checking state values for + * @param block In which block is this operation */ - public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstruction.Op labelOp) { - if (instruction instanceof RAVInstruction.Op op) { - checkInputs(op.uses, op, block, labelOp); - checkInputs(op.alive, op, block, labelOp); - - for (int i = 0; i < op.temp.count; i++) { - var curr = op.temp.curr[i]; - var orig = op.temp.orig[i]; - - if (!kindsEqual(orig, curr)) { - // Make sure the assigned register has the correct kind for temp. - throw new KindsMismatchException(instruction.lirInstruction, block, orig, curr, true); + protected void checkStateJavaKinds(RAVInstruction.Op op, BasicBlock block) { + int kindIdx = 0; + for (int i = 0; i < op.stateValues.count; i++) { + var orig = op.stateValues.curr[i]; + var curr = op.stateValues.curr[i]; + + JavaKind kind = null; + while (kindIdx < op.kinds.length) { + JavaKind target = op.kinds[kindIdx++]; + if (!JavaKind.Illegal.equals(target)) { + kind = target; + break; } + // Illegal values are ignored when iterating over state values + // but kept in the kinds array so we need to skip them. } - this.checkInputs(op.stateValues, op, block, labelOp); - - int kindIdx = 0; - for (int i = 0; i < op.stateValues.count; i++) { - var orig = op.stateValues.orig[i]; - var curr = op.stateValues.curr[i]; + if (kind == null) { + break; // We no longer have a JavaKind for other values + } - var state = this.values.get(curr); - if (state instanceof ValueAllocationState valueAllocationState && valueAllocationState.getRAValue().equals(orig)) { + var origLIRKind = orig.getLIRKind(); + var currLIRKind = curr.getLIRKind(); + if (JavaKind.Object.equals(kind)) { + if (!origLIRKind.isValue() && !currLIRKind.isValue()) { continue; } - JavaKind kind = null; - while (kindIdx < op.kinds.length) { - JavaKind target = op.kinds[kindIdx++]; - if (!JavaKind.Illegal.equals(target)) { - kind = target; - break; - } - // Illegal values are ignored when iterating over state values - // but kept in the kinds array so we need to skip them. + throw new RAVException(orig.getValue() + " -> " + curr.getValue() + " not an object java kind when marked as a reference"); + } else { + if (origLIRKind.isValue() && currLIRKind.isValue()) { + continue; } - if (!JavaKind.Object.equals(kind)) { + throw new RAVException(orig.getValue() + " -> " + curr.getValue() + " is a reference when not marked as an object java kind"); + } + } + } + + /** + * Make sure concrete current locations changed by the allocator + * are not violating set of LIRInstruction.OperandFlag flags, + * which specify what type can they be. This is done on every + * array of pairs (dest, uses, alive, temp). + * + * @param values Value array pair we are verifying + * @param op Instruction which holds this array, for tracing in exceptions + * @param block Block this instruction is in, for tracing in exceptions + * @throws OperandFlagMismatchException Operand is a wrong type based on OperandFlag set. + */ + protected void checkOperandFlags(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { + for (int i = 0; i < values.count; i++) { + var curr = values.curr[i]; + if (curr == null) { + continue; + } + + var flags = values.operandFlags.get(i); + var currValue = curr.getValue(); + if (LIRValueUtil.isStackSlotValue(currValue)) { + if (flags.contains(LIRInstruction.OperandFlag.STACK)) { continue; } - // Maybe check correspondence to JavaKind? - // Object -> has ref type in LIRKind? - - var state = this.values.get(curr); - if (state.isUnknown() || state.isConflicted()) { + } else if (ValueUtil.isRegister(currValue)) { + if (flags.contains(LIRInstruction.OperandFlag.REG)) { continue; } - - var instr = op.lirInstruction; - - var valueAllocState = (ValueAllocationState) state; - var source = valueAllocState.getSource(); - if (source == null) { - throw new IllegalStateException(); + } else if (LIRValueUtil.isConstantValue(currValue)) { + if (flags.contains(LIRInstruction.OperandFlag.CONST)) { + continue; + } + } else if (Value.ILLEGAL.equals(currValue)) { + if (flags.contains(LIRInstruction.OperandFlag.ILLEGAL)) { + continue; } - - var v = valueAllocState.getValue(); - // How to check type is a reference? - - // Safepoint update -> change refered objects to unknown in-case GC cleared them? - // Safepoint check -> check for correspondence to JavaKind - // + checking correspondence to state } - this.checkAliveConstraint(op, block); + throw new OperandFlagMismatchException(op, block, currValue, flags); } } @@ -380,10 +430,6 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { assert op.dests.orig[i] != null; if (op.dests.curr[i] == null) { - if (phiResolution == PhiResolution.FromJump) { - throw new LabelNotResolvedError(block, op, phiResolution); - } - continue; } @@ -399,6 +445,9 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { } } + // For calls, we hope that all saved registers are listed, even + // though they are not as per checking RegisterConfig.getCallerSavedRegisters() + // but using said list to clear them causes issues. for (int i = 0; i < op.temp.count; i++) { var value = op.temp.curr[i]; if (value.isIllegal()) { @@ -412,15 +461,63 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { } /** - * Update state with a ValueMove. + * Update block state with a safe point list of live references deemed by the GC, + * any other references not included in said list are to be set as unknown so + * there's no freed pointer use. + *

+ * Not in use currently, because we do not have a reliable list of live references. + *

+ * Reference test case: AOTTutorial + * + * @param references List of references deemed as live by the GC + */ + protected void updateWithSafePoint(List references) { + List toRemove = new ArrayList<>(); + for (var entry : this.values.internalMap.entrySet()) { + var state = entry.getValue(); + if (state.isUnknown() || state.isConflicted()) { + continue; // Do not care, retain information + } + + var valueAllocState = (ValueAllocationState) state; + if (valueAllocState.getValue().getValueKind(LIRKind.class).isValue()) { + continue; // Not a reference, continue + } + + boolean referenceFound = false; + for (var reference : references) { + if (reference.equals(entry.getKey())) { + referenceFound = true; + break; + } + } + + if (referenceFound) { + continue; + } + + // Remove all references that are not present in the references list, + // maybe it makes sense to keep registers that have live references, + // that are same as the one in references list? Because said list + // is expected to have stack slots and registers can retain same references. + this.values.put(entry.getKey(), UnknownAllocationState.INSTANCE); + } + } + + /** + * Update state with a ValueMove, if locations is concrete, + * we set it to a variable/constant, if it's a variable to variable + * move, then all locations containing old variable need to be changed + * to the new variable. * * @param valueMove Value move we update state from * @param block Block where it is located */ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove, BasicBlock block) { - if (ValueUtil.isRegister(valueMove.location.getValue())) { - this.values.put(valueMove.location, new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); - } else if (valueMove.location.isVariable()) { + if (valueMove.location.isVariable()) { + // Whenever there is a move between two variables, + // we need to change every location containing the old variable (rhs - source) + // to the new variable (lhs - destination) // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD // Move before allocation // TestCase: BoxingTest.boxBoolean diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java new file mode 100644 index 000000000000..df0e4d1c0ece --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.meta.Value; + +import java.util.EnumSet; + +/** + * Value used in instruction does not satisfy it's operand flags, + * for example if an operand is a stack slot, but should only + * be a register. + */ +@SuppressWarnings("serial") +public class OperandFlagMismatchException extends RAVException { + public RAVInstruction.Op instruction; + public BasicBlock block; + public Value value; + public EnumSet flags; + + public OperandFlagMismatchException(RAVInstruction.Op op, BasicBlock block, Value value, EnumSet flags) { + super(getErrorMesage(op, block, value, flags)); + this.instruction = op; + this.block = block; + this.value = value; + this.flags = flags; + } + + static String getErrorMesage(RAVInstruction.Op op, BasicBlock block, Value value, EnumSet flags) { + return "Value " + value + " does not satisfy operand flags " + flags.toString() + " in " + op.lirInstruction + " in " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java index 6c9f1cf30b18..235724af5483 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java @@ -1,37 +1,39 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; /** * Label variable location resolution method. */ public enum PhiResolution { - /** - * Before block is entered, locations are determined - * by its predecessors with state defined and variables - * used in their Jump instructions. - */ - FromJump, - /** - * By looking up variables first usage, - * and walking back to defining label - - * per each variable. - */ - FromUsage, - /** - * Resolve locations by looking at states of predecessors - * and converging on a single location. - */ - FromPredecessors, /** * By looking up variables first usage, * and walking back to defining label - * every variable at once. */ FromUsageGlobal, - /** - * Resolve locations by resolving - * conflicts created by numerous predecessors. - */ - FromConflicts, /** * Modify the allocator to keep * label variable locations present diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 9e61cece5c2c..5573ef09ef26 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -1,30 +1,44 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.InstructionStateProcedure; -import jdk.graal.compiler.lir.InstructionValueProcedure; 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.StandardOp; -import jdk.graal.compiler.lir.ValueProcedure; -import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.lir.gen.LIRGenerationResult; -import jdk.graal.compiler.lir.gen.LIRGenerator; import jdk.graal.compiler.lir.phases.AllocationPhase; import jdk.graal.compiler.util.EconomicHashMap; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.meta.Value; +import jdk.vm.ci.code.ValueUtil; +import org.graalvm.collections.Equivalence; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; import java.util.Map; /** @@ -32,77 +46,15 @@ * about variables/constants used in LIR instructions. */ public class PreRegisterAllocationPhase extends AllocationPhase { - /** - * Factory to produce tagged constants that act like variables. - */ - protected TaggedConstantFactory taggedConstantFactory; /** * Shared state with the verification phase. */ protected RegisterAllocationVerifierPhaseState state; public PreRegisterAllocationPhase(RegisterAllocationVerifierPhaseState state) { - this.taggedConstantFactory = new TaggedConstantFactory(); this.state = state; } - /** - * Overwrite a constant with a tagged one. - */ - public static class ConstantOverrideValueProcedure implements ValueProcedure { - private final LIR lir; - private List variables; - private final Map constantValueMap; - private final Method nextVariableMethod; - - public ConstantOverrideValueProcedure(LIR lir, Map constantValueMap) { - this.lir = lir; - this.constantValueMap = constantValueMap; - - try { - this.nextVariableMethod = LIRGenerator.VariableProvider.class.getDeclaredMethod("nextVariable"); - this.nextVariableMethod.setAccessible(true); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - public void setVariableList(List variables) { - this.variables = variables; - } - - public int nextVariable() { - try { - return (int) nextVariableMethod.invoke((LIRGenerator.VariableProvider) this.lir); - } catch (InvocationTargetException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (value instanceof ConstantValue constant) { - var variable = new Variable(constant.getValueKind(), nextVariable()); - constantValueMap.put(variable, constant); - variables.add(variable); - return variable; - } - - return value; - } - } - - protected InstructionValueProcedure taggedConstantValueProc = new InstructionValueProcedure() { - @Override - public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (value instanceof ConstantValue constantValue) { - return new ConstantValue(constantValue.getValueKind(), taggedConstantFactory.createNew(constantValue.getConstant())); - } - - return value; - } - }; - /** * Process every block and every instruction to save variables used in them for the verification procedure. * @@ -117,27 +69,15 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo } var preallocMap = state.createInstructionMap(lirGenRes); + Map preallocMap = new EconomicHashMap<>(Equivalence.IDENTITY); LIR lir = lirGenRes.getLIR(); - Map constantValueMap = new EconomicHashMap<>(); - var constOverwriteProc = new ConstantOverrideValueProcedure(lir, constantValueMap); for (var blockId : lir.getBlocks()) { BasicBlock block = lir.getBlockById(blockId); ArrayList instructions = lir.getLIRforBlock(block); - List newVars = new ArrayList<>(); - constOverwriteProc.setVariableList(newVars); - RAVInstruction.Base previousInstr = null; for (var instruction : instructions) { - if (instruction instanceof StandardOp.JumpOp && this.state.phiResolution == PhiResolution.FromJump) { - if (this.state.moveConstants) { - instruction.forEachAlive(constOverwriteProc); - } else { - instruction.forEachAlive(taggedConstantValueProc); - } - } - if (this.isVirtualMove(instruction)) { // Virtual moves (variable = MOV real register) are going to be removed by the allocator, // but we still need the information about which variables are associated to which real @@ -199,23 +139,6 @@ public void doState(LIRInstruction instruction, LIRFrameState state) { previousInstr = opRAVInstr; } } - - if (newVars.isEmpty()) { - continue; - } - - var j = instructions.removeLast(); - var it = newVars.iterator(); - while (it.hasNext()) { - var v = LIRValueUtil.asVariable(it.next()); - var mov = context.spillMoveFactory.createMove(v, constantValueMap.get(v)); - var ravInstr = new RAVInstruction.Op(mov); - mov.forEachOutput(ravInstr.dests.copyOriginalProc); - preallocMap.put(mov, ravInstr); - instructions.add(mov); - } - - instructions.add(j); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java new file mode 100644 index 000000000000..38898d88686f --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterValue; + +public class RARegister extends RAValue { + protected RegisterValue registerValue; + + protected RARegister(RegisterValue registerValue) { + super(registerValue); + + this.registerValue = registerValue; + } + + public RegisterValue getRegisterValue() { + return registerValue; + } + + public Register getRegister() { + return registerValue.getRegister(); + } + + @Override + public RARegister asRegister() { + return this; + } + + @Override + public boolean isRegister() { + return true; + } + + @Override + public int hashCode() { + return this.registerValue.getRegister().hashCode(); + } + + /** + * Equal RegisterValue on it's Register, not Register and kind, + * otherwise same as Value. + * + * @param other The reference object with which to compare. + * @return Are said values equal? + */ + @Override + public boolean equals(Object other) { + if (other instanceof RARegister otherReg) { + return this.registerValue.getRegister().equals(otherReg.registerValue.getRegister()); + } + + return false; + } + + @Override + public String toString() { + return this.registerValue.getRegister().toString(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java index 998922734a3d..9fca8ada5fc6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java @@ -36,4 +36,8 @@ public class RAVException extends GraalError { public RAVException(String message) { super(message); } + + public RAVException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 74d6a71e3ab3..d12beba3976b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -24,16 +24,20 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.hotspot.amd64.AMD64HotSpotSafepointOp; import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.VirtualStackSlot; +import jdk.graal.compiler.lir.aarch64.AArch64Call; +import jdk.graal.compiler.lir.amd64.AMD64Call; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Value; +import java.util.ArrayList; import java.util.EnumSet; -import java.util.LinkedList; import java.util.List; public class RAVInstruction { @@ -73,8 +77,8 @@ public abstract static class Base { public Base(LIRInstruction lirInstruction) { this.lirInstruction = lirInstruction; - this.virtualMoveList = new LinkedList<>(); - this.speculativeMoveList = new LinkedList<>(); + this.virtualMoveList = new ArrayList<>(); + this.speculativeMoveList = new ArrayList<>(); } public LIRInstruction getLIRInstruction() { @@ -105,20 +109,24 @@ public static class ValueArrayPair { /** * Array of size count holding original variables before allocation. */ - protected RAValue[] orig; + public RAValue[] orig; /** * Array of size count holding current locations used after allocation. */ - protected RAValue[] curr; + public RAValue[] curr; + + public ArrayList> operandFlags; + /** * Number of pairs of values stored here. */ - protected int count; + public int count; public ValueArrayPair(int count) { this.count = count; this.curr = new RAValue[count]; this.orig = new RAValue[count]; + this.operandFlags = new ArrayList<>(count); } public InstructionValueProcedure copyOriginalProc = new InstructionValueProcedure() { @@ -127,6 +135,7 @@ public ValueArrayPair(int count) { @Override public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { ValueArrayPair.this.addOrig(index, value); + ValueArrayPair.this.operandFlags.add(flags); index++; return value; } @@ -138,6 +147,11 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope @Override public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { ValueArrayPair.this.addCurrent(index, value); + + if (index < operandFlags.size() && !operandFlags.get(index).equals(flags)) { + throw new IllegalStateException(); + } + index++; return value; } @@ -155,9 +169,8 @@ public RAValue getOrig(int index) { public void addCurrent(int index, Value value) { if (index >= this.count) { - // TestCase: DerivedOopTest - // liveBasePointers has extra item after register allocation - // TODO: handle extra items here + // In test case DerivedOopTest, liveBasePointers has extra + // values after allocation in frame state, so we skip them return; } @@ -293,6 +306,22 @@ public boolean hasMissingDefinitions() { return false; } + public boolean isLabel() { + return lirInstruction instanceof StandardOp.LabelOp; + } + + public boolean isJump() { + return lirInstruction instanceof StandardOp.JumpOp; + } + + public boolean isCall() { + return lirInstruction instanceof AMD64Call.CallOp || lirInstruction instanceof AArch64Call.CallOp; + } + + public boolean isSafePoint() { + return lirInstruction instanceof AMD64HotSpotSafepointOp; + } + @Override public String toString() { return this.dests.toString() + " = Op " + this.uses.toString() + " " + this.alive.toString() + " " + this.temp.toString(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index 1dd5509a1d53..dc2f3e02d2ae 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -24,8 +24,8 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; @@ -51,6 +51,10 @@ public static RAValue create(Value value) { return new RAVariable(LIRValueUtil.asVariable(value)); } + if (ValueUtil.isRegister(value)) { + return new RARegister(ValueUtil.asRegisterValue(value)); + } + return new RAValue(value); } @@ -76,10 +80,22 @@ public boolean isVariable() { return false; } + public boolean isRegister() { + return false; + } + + public RARegister asRegister() { + return (RARegister) this; + } + + public LIRKind getLIRKind() { + return value.getValueKind(LIRKind.class); + } + @Override public int hashCode() { - if (ValueUtil.isRegister(this.value)) { - return ValueUtil.asRegisterValue(this.value).getRegister().hashCode(); + if (LIRValueUtil.isVirtualStackSlot(this.value)) { + return LIRValueUtil.asVirtualStackSlot(this.value).getId(); } return this.value.hashCode(); @@ -95,10 +111,8 @@ public int hashCode() { @Override public boolean equals(Object other) { if (other instanceof RAValue otherValueWrap) { - if (ValueUtil.isRegister(this.value) && ValueUtil.isRegister(otherValueWrap.value)) { - var thisReg = ValueUtil.asRegisterValue(this.value); - var otherReg = ValueUtil.asRegisterValue(otherValueWrap.value); - return thisReg.getRegister().equals(otherReg.getRegister()); + if (LIRValueUtil.isVirtualStackSlot(this.value) && otherValueWrap.value.equals(this.value)) { + return LIRValueUtil.asVirtualStackSlot(this.value).getId() == LIRValueUtil.asVirtualStackSlot(otherValueWrap.value).getId(); } return this.value.equals(otherValueWrap.value); @@ -109,8 +123,8 @@ public boolean equals(Object other) { @Override public String toString() { - if (ValueUtil.isRegister(this.value)) { - return ValueUtil.asRegisterValue(this.value).getRegister().toString(); + if (LIRValueUtil.isVirtualStackSlot(this.value)) { + return "vstack:" + LIRValueUtil.asVirtualStackSlot(this.value).getId(); } return value.toString(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index 731e104fa1ba..ad59b8520a96 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -29,8 +29,8 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -74,10 +74,6 @@ public class RegisterAllocationVerifier { */ protected PhiResolution phiResolution; - protected FromPredecessorsResolver fromPredecessorsResolver; - protected FromJumpResolver fromJumpResolver; - protected FromUsageResolver fromUsageResolver; - /** * Resolves locations for label variables by finding * their first usage and walking back to the defining @@ -89,14 +85,12 @@ public class RegisterAllocationVerifier { * Conflict resolver for re-materialized constants. */ protected ConflictResolver constantMaterializationConflictResolver; - protected ConflictResolver labelConflictResolver; public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; this.registerAllocationConfig = registerAllocationConfig; this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); - this.labelConflictResolver = new LabelConflictResolver(); var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; @@ -105,40 +99,9 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.blockStates = new BlockMap<>(cfg); this.phiResolution = phiResolution; - this.fromPredecessorsResolver = new FromPredecessorsResolver(lir, blockInstructions, blockStates, blockEntryStates); - this.fromJumpResolver = new FromJumpResolver(lir, blockInstructions, blockStates); - this.fromUsageResolver = new FromUsageResolver(lir, blockInstructions); this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); } - /** - * Do all predecessors have state defined for this block? - * Meaning they were processed by the entry block calculation. - * - * @param block Block for which we look at predecessors - * @return true, if all predecessors of said block have a state defined. - */ - private boolean doPrecessorsHaveStates(BasicBlock block) { - for (int i = 0; i < block.getPredecessorCount(); i++) { - var pred = block.getPredecessorAt(i); - if (this.blockStates.get(pred) == null) { - return false; - } - } - return true; - } - - /** - * Does this instruction have locations missing - * in it's output array pair? - * - * @param op Operation we are looking at - a label - * @return true, if there is at least one missing location, otherwise false. - */ - private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { - return op.hasMissingDefinitions(); - } - /** * For every block, we need to calculate its entry state * which is a combination of states of blocks that are its @@ -147,59 +110,28 @@ private boolean hasMissingRegistersInLabel(RAVInstruction.Op op) { * on instructions before allocation. */ public void calculateEntryBlocks() { - Queue> worklist = new LinkedList<>(); + Queue> worklist = new ArrayDeque<>(); - this.blockEntryStates.put(this.lir.getControlFlowGraph().getStartBlock(), new MergedBlockVerifierState(registerAllocationConfig, phiResolution, constantMaterializationConflictResolver, labelConflictResolver)); + var startBlock = this.lir.getControlFlowGraph().getStartBlock(); + this.blockEntryStates.put(startBlock, new MergedBlockVerifierState(startBlock, registerAllocationConfig, phiResolution, constantMaterializationConflictResolver)); worklist.add(this.lir.getControlFlowGraph().getStartBlock()); while (!worklist.isEmpty()) { var block = worklist.poll(); var instructions = this.blockInstructions.get(block); - boolean resolvedPhi = false; - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); - if (this.phiResolution == PhiResolution.FromPredecessors && this.doPrecessorsHaveStates(block) && this.hasMissingRegistersInLabel(labelInstr)) { - resolvedPhi = this.fromPredecessorsResolver.resolvePhiFromPredecessors(block, labelInstr); - } - - if (this.phiResolution == PhiResolution.FromJump && this.hasMissingRegistersInLabel(labelInstr)) { - boolean skip = false; - for (int i = 0; i < block.getPredecessorCount(); i++) { - var pred = block.getPredecessorAt(i); - if (worklist.contains(pred)) { - skip = true; - worklist.add(block); - break; - } - } - - if (skip) { - continue; - } - - this.fromJumpResolver.resolvePhi(block); - } - // Create new entry state for successor blocks out of current block state - var state = new MergedBlockVerifierState(this.blockEntryStates.get(block), registerAllocationConfig, phiResolution, constantMaterializationConflictResolver, labelConflictResolver); + var state = new MergedBlockVerifierState(block, this.blockEntryStates.get(block)); for (var instr : instructions) { state.update(instr, block); } this.blockStates.put(block, state); - // if (this.phiResolution == PhiResolution.FromJump) { - // this.fromJumpResolver.resolvePhiFromJump(block); - // } - - if (resolvedPhi) { - this.fromPredecessorsResolver.propagateLabelChangeFromPredecessors(block); - } - for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); if (this.blockEntryStates.get(succ) == null) { - var succState = new MergedBlockVerifierState(state, registerAllocationConfig, phiResolution, constantMaterializationConflictResolver, labelConflictResolver); + var succState = new MergedBlockVerifierState(succ, state); this.blockEntryStates.put(succ, succState); worklist.remove(succ); @@ -208,63 +140,13 @@ public void calculateEntryBlocks() { } var succState = this.blockEntryStates.get(succ); - var succLabelOp = (RAVInstruction.Op) this.blockInstructions.get(succ).getFirst(); - if (succState.meetWith(state) || this.hasMissingRegistersInLabel(succLabelOp)) { - // if we resolved a phi, then we also need to process children again, - // TODO: might be better to do something in propagateLabelChangeFromPredecessors - + if (succState.meetWith(state)) { // State changed or labels have not yet been determined, add to worklist this.blockEntryStates.put(succ, succState); worklist.remove(succ); // Always at the end, for predecessors to be processed first. worklist.add(succ); } } - - if (phiResolution == PhiResolution.FromPredecessors && worklist.isEmpty()) { - this.addMissingLabelBlocks(worklist); - } - } - } - - private int missingLabelBlocksCount = -1; - private int missingLabelCheckRunCount = 0; - - /** - * This method needs to be run, because sometimes already processed blocks - * do not get their labels updated with newly defined variables and thus - * resolution is not complete. - *

- * TODO: this might be better be handled by some dependency graph - * - * @param worklist Worklist to which we add new blocks for processing. - */ - private void addMissingLabelBlocks(Queue> worklist) { - if (missingLabelBlocksCount != -1 && missingLabelBlocksCount >= missingLabelCheckRunCount) { - // First time around it counts number of missing label blocks - // then it uses said count to make sure this function is not ran more - // than said amount of times to prevent infinite cycles, - // this can happen if there's a dependency loop between said label blocks - // and should be avoided with other resolution methods. - return; - } - - missingLabelCheckRunCount++; - int currentMissingLabelBlockCount = 0; - for (var blockId : this.lir.getBlocks()) { - var pBlock = this.lir.getBlockById(blockId); - var label = (RAVInstruction.Op) this.blockInstructions.get(pBlock).getFirst(); - if (this.hasMissingRegistersInLabel(label)) { - if (!this.doPrecessorsHaveStates(pBlock)) { - throw new LabelNotResolvedError(pBlock, label, phiResolution); - } - - currentMissingLabelBlockCount++; - worklist.add(pBlock); - } - } - - if (missingLabelBlocksCount == -1) { - missingLabelBlocksCount = currentMissingLabelBlockCount; } } @@ -324,14 +206,6 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { */ public void run() { this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); - if (this.phiResolution == PhiResolution.FromConflicts) { - this.labelConflictResolver.prepare(lir, blockInstructions); - } - - if (this.phiResolution == PhiResolution.FromUsage) { - this.fromUsageResolver.resolvePhiFromUsage(); - } - if (this.phiResolution == PhiResolution.FromUsageGlobal) { this.fromUsageResolverGlobal.resolvePhiFromUsage(); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java index 8cfd206489af..f31f99b7aef4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -39,7 +39,6 @@ */ public class RegisterAllocationVerifierPhaseState { public PhiResolution phiResolution; - public boolean moveConstants; public String filterStr; /** @@ -51,7 +50,6 @@ public RegisterAllocationVerifierPhaseState(OptionValues options) { this.verifierInstructions = new EconomicHashMap<>(Equivalence.IDENTITY); this.phiResolution = RegisterAllocationVerifierPhase.Options.RAPhiResolution.getValue(options); - this.moveConstants = RegisterAllocationVerifierPhase.Options.MoveConstants.getValue(options); this.filterStr = RegisterAllocationVerifierPhase.Options.RAFilter.getValue(options); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index 327b511bfd29..120e5ef7ec71 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -24,6 +24,8 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; + /** * Default allocation state for all locations, * nothing was yet inserted. @@ -47,18 +49,18 @@ public boolean isUnknown() { * @return Unknown if both are, otherwise a conflict */ @Override - public AllocationState meet(AllocationState other) { + public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { if (other.isUnknown()) { return this; } if (other instanceof ConflictedAllocationState conflictedState) { var newConfState = new ConflictedAllocationState(conflictedState.conflictedStates); - newConfState.addConflictedValue(ValueAllocationState.createIllegal()); + newConfState.addConflictedValue(ValueAllocationState.createIllegal(block)); return newConfState; } - return new ConflictedAllocationState((ValueAllocationState) other, ValueAllocationState.createIllegal()); + return new ConflictedAllocationState((ValueAllocationState) other, ValueAllocationState.createIllegal(null)); } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 2e0b1809f49d..91a5079feafe 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -71,9 +71,8 @@ public ValueAllocationState(ValueAllocationState other) { * * @return instance of ValueAllocationState holding Value.ILLEGAL. */ - public static ValueAllocationState createIllegal() { - // TODO: pass in the block that created this value - return new ValueAllocationState(new RAValue(Value.ILLEGAL), null, null); + public static ValueAllocationState createIllegal(BasicBlock block) { + return new ValueAllocationState(new RAValue(Value.ILLEGAL), null, block); } public Value getValue() { @@ -100,12 +99,12 @@ public BasicBlock getBlock() { * @param other Other state coming from a predecessor edge * @return ValueAllocationState if their contents are equal, otherwise ConflictedAllocationState. */ - public AllocationState meet(AllocationState other) { + public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { if (other.isUnknown()) { // Unknown is coming from different predecessor where this location // is undefined, meaning this value is not always accessible in the successor // and thus conflict is created. - return new ConflictedAllocationState(createIllegal(), this); + return new ConflictedAllocationState(createIllegal(otherBlock), this); } if (other.isConflicted()) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index 95667d7f79d7..b49287f4f908 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -71,6 +71,9 @@ public static void print(PrintStream out, LIR lir, BlockMap 0) { + out.println("\t\t State: " + op.stateValues); + } } out.println(); } From 984a91f3006a64fef990c81a532850492efebd26 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 2 Mar 2026 22:57:53 +0100 Subject: [PATCH 056/112] Add tests for the verifier --- .../core/test/RegAllocVerifierTest.java | 1096 +++++++++++++++++ 1 file changed, 1096 insertions(+) create mode 100644 compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java new file mode 100644 index 000000000000..06bf61bcb599 --- /dev/null +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -0,0 +1,1096 @@ +/* + * Copyright (c) 2026, 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.core.test; + +import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.ValueProcedure; +import jdk.graal.compiler.lir.Variable; +import jdk.graal.compiler.lir.alloc.RegisterAllocationPhase; +import jdk.graal.compiler.lir.alloc.verifier.AliveConstraintViolationException; +import jdk.graal.compiler.lir.alloc.verifier.ConflictedAllocationState; +import jdk.graal.compiler.lir.alloc.verifier.InvalidRegisterUsedException; +import jdk.graal.compiler.lir.alloc.verifier.KindsMismatchException; +import jdk.graal.compiler.lir.alloc.verifier.PreRegisterAllocationPhase; +import jdk.graal.compiler.lir.alloc.verifier.RAVException; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; +import jdk.graal.compiler.lir.alloc.verifier.RAValue; +import jdk.graal.compiler.lir.alloc.verifier.RAVariable; +import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhase; +import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhaseState; +import jdk.graal.compiler.lir.alloc.verifier.ValueAllocationState; +import jdk.graal.compiler.lir.alloc.verifier.ValueNotInRegisterException; +import jdk.graal.compiler.lir.gen.LIRGenerationResult; +import jdk.graal.compiler.lir.phases.AllocationPhase; +import jdk.graal.compiler.lir.phases.LIRPhase; +import jdk.graal.compiler.lir.phases.LIRSuites; +import jdk.graal.compiler.options.OptionValues; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.Value; +import jdk.vm.ci.meta.ValueKind; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RegAllocVerifierTest extends GraalCompilerTest { + /** + * Should the valid set of compiler phase suites be used? + */ + boolean validSuites = true; + + /** + * Exception thrown during verification process. + */ + RAVException exception; + + /** + * Phase that causes RAVException to be thrown, + * by modifying LIR or Verifier State. + */ + LIRPhase phase; + + interface VerifierStateUser { + /** + * Set the currently used verification state to phase that + * implements this interface in-order for it to facilitate + * a change that will make the verifier find an exception. + * + * @param state Verifier state shared across it's phases + */ + void setState(RegisterAllocationVerifierPhaseState state); + } + + class RAVPhaseWrapper extends RegisterAllocationVerifierPhase { + RAVPhaseWrapper(RegisterAllocationVerifierPhaseState state) { + super(state); + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { + try { + super.run(target, lirGenRes, context); + } catch (RAVException e) { + exception = e; + } + } + } + + /** + * Overwrites a destination variable with a newly created one + * to cause a ValueNotInLocationException with old variable being + * in said place. + */ + class ChangeVariablePhase extends LIRPhase implements VerifierStateUser { + protected RAVariable originalVariable; + protected RAVariable newVariable; + protected RegisterAllocationVerifierPhaseState state; + + @Override + public void setState(RegisterAllocationVerifierPhaseState state) { + this.state = state; + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { + var lir = lirGenRes.getLIR(); + // var instrMap = this.state.getInstructionMap(lirGenRes); + var instrMap = lirGenRes.getVIR(); + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + if (instruction instanceof StandardOp.LabelOp) { + continue; + } + + var virInstr = instrMap.get(instruction); + if (virInstr instanceof RAVInstruction.Op op && changeVariable(lirGenRes, op)) { + return; + } + } + } + } + + protected boolean changeVariable(LIRGenerationResult lirGenRes, RAVInstruction.Op op) { + for (int i = 0; i < op.dests.count; i++) { + if (!op.dests.orig[i].isVariable()) { + continue; + } + + var variable = op.dests.orig[i].asVariable(); + var newVariable = createNewVariable(lirGenRes, variable); + + op.dests.orig[i] = newVariable; + + this.originalVariable = variable; + this.newVariable = newVariable; + + return true; + } + + return false; + } + + protected RAVariable createNewVariable(LIRGenerationResult lirGenRes, RAVariable oldVariable) { + var newVariable = new Variable(oldVariable.getValue().getValueKind(), lirGenRes.getLIR().numVariables() + 1); + return (RAVariable) RAValue.create(newVariable); + } + } + + /** + * This pass changes register allocation config to only allow certain + * registers to be used for allocation, and we want the verifier to + * detect usage of said register. + */ + class DisallowedRegisterPhase extends LIRPhase { + protected Register ignoredReg; + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { + var lir = lirGenRes.getLIR(); + + var restrictedTo = getAllocationRestrictedToArray(lir, context.registerAllocationConfig); + context.registerAllocationConfig = new RegisterAllocationConfig(context.registerAllocationConfig.getRegisterConfig(), restrictedTo); + } + + protected void modifyAllocatableRegisterList(LIR lir, List registerList) { + registerList.remove(findAllocatedRegister(lir)); + } + + protected String[] getAllocationRestrictedToArray(LIR lir, RegisterAllocationConfig cfg) { + var regAllocatedTo = findAllocatedRegister(lir); + var allocatableRegs = cfg.getAllocatableRegisters(); + String[] restrictedTo = new String[allocatableRegs.size() - 1]; + int i = 0; + for (var reg : allocatableRegs) { + if (i >= allocatableRegs.size() - 1) { + continue; + } + + if (reg.equals(regAllocatedTo)) { + continue; + } + + restrictedTo[i++] = reg.toString(); + } + + ignoredReg = regAllocatedTo; + return restrictedTo; + } + + protected Register findAllocatedRegister(LIR lir) { + final Register[] allocatedTo = {null}; + for (var blockId : lir.getBlocks()) { + if (allocatedTo[0] != null) { + break; + } + + var block = lir.getBlockById(blockId); + var instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + if (instruction instanceof StandardOp.LabelOp) { + continue; + } + + instruction.forEachOutput(new ValueProcedure() { + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (value instanceof RegisterValue regValue) { + allocatedTo[0] = regValue.getRegister(); + } + return value; + } + }); + + if (allocatedTo[0] != null) { + break; + } + } + } + return allocatedTo[0]; + } + } + + /** + * Change a register that is only used once as output, + * so that state of it is Unknown. + */ + class ForceUnknownStateInRegister extends LIRPhase implements VerifierStateUser { + protected RAVariable variable; + protected RegisterValue oldRegister = null; + protected RegisterValue newRegister; + protected RegisterAllocationVerifierPhaseState state; + protected int idx = 0; + + ForceUnknownStateInRegister() { + this.regUsage = new EconomicHashMap<>(); + } + + @Override + public void setState(RegisterAllocationVerifierPhaseState state) { + this.state = state; + } + + protected Map regUsage; + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { + var lir = lirGenRes.getLIR(); + var instrMap = this.state.getInstructionMap(lirGenRes); + + var replacementReg = getUnusedAllowedRegister(lir, context.registerAllocationConfig); + + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + if (!(instruction instanceof StandardOp.LabelOp) && instrMap.containsKey(instruction)) { + var op = (RAVInstruction.Op) instrMap.get(instruction); + + if (handleOp(op, replacementReg)) { + return; + } + + if (handleVirtualMoves(op, getUnusedAllowedRegister(lir, context.registerAllocationConfig))) { + return; + } + } + + handleRegisterUsage(instruction); + } + } + } + + protected boolean handleOp(RAVInstruction.Op op, Register replacementReg) { + idx = 0; + op.getLIRInstruction().forEachOutput(new ValueProcedure() { + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (ValueUtil.isRegister(value) && op.dests.orig[idx].isVariable() && oldRegister == null && !regUsage.containsKey(ValueUtil.asRegister(value))) { + oldRegister = ValueUtil.asRegisterValue(value); + newRegister = replacementReg.asValue(oldRegister.getValueKind()); + variable = op.dests.orig[idx].asVariable(); + return newRegister; + } + + idx++; + return value; + } + }); + + return newRegister != null; + } + + protected boolean handleVirtualMoves(RAVInstruction.Op op, Register replacementReg) { + for (var move : op.getVirtualMoveList()) { + var locValue = move.location.getValue(); + if (ValueUtil.isRegister(locValue)) { + var register = ValueUtil.asRegister(locValue); + + if (move.variableOrConstant.isVariable() && !regUsage.containsKey(register)) { + oldRegister = ValueUtil.asRegisterValue(locValue); + newRegister = replacementReg.asValue(locValue.getValueKind()); + variable = move.variableOrConstant.asVariable(); + move.location = RAValue.create(newRegister); + return true; + } + + regUsage.put(register, regUsage.getOrDefault(register, 1) + 1); + } + } + return false; + } + + protected void handleRegisterUsage(LIRInstruction instruction) { + instruction.forEachOutput(new ValueProcedure() { + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (ValueUtil.isRegister(value)) { + var register = ValueUtil.asRegister(value); + regUsage.put(register, regUsage.getOrDefault(register, 1) + 1); + } + + return value; + } + }); + } + + protected Register getUnusedAllowedRegister(LIR lir, RegisterAllocationConfig cfg) { + Map usedRegisters = new EconomicHashMap<>(); + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + instruction.forEachOutput(new ValueProcedure() { + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (ValueUtil.isRegister(value)) { + var register = ValueUtil.asRegister(value); + usedRegisters.put(register, usedRegisters.getOrDefault(register, 1) + 1); + } + + return value; + } + }); + } + } + + for (var reg : cfg.getAllocatableRegisters()) { + if (usedRegisters.containsKey(reg)) { + continue; + } + + return reg; + } + + return null; + } + + } + + /** + * Change kind of an operand to trigger a KindsMismatchException, + * very simply, find first instruction that is not a label and + * look through its operand array to find first variable and + * change its type to Illegal. + */ + abstract class ChangeKindPhase extends LIRPhase implements VerifierStateUser { + protected RegisterAllocationVerifierPhaseState state; + protected Variable variable; + + @Override + public void setState(RegisterAllocationVerifierPhaseState state) { + this.state = state; + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { + var lir = lirGenRes.getLIR(); + var instrMap = this.state.getInstructionMap(lirGenRes); + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + if (instruction instanceof StandardOp.LabelOp) { + continue; + } + + var op = (RAVInstruction.Op) instrMap.get(instruction); + if (changeVariableKind(getTargetValueArrayPair(op))) { + return; + } + } + } + } + + protected abstract RAVInstruction.ValueArrayPair getTargetValueArrayPair(RAVInstruction.Op op); + + protected boolean changeVariableKind(RAVInstruction.ValueArrayPair values) { + for (int i = 0; i < values.count; i++) { + var orig = values.orig[i]; + if (!orig.isVariable()) { + continue; + } + + var raVar = orig.asVariable(); + var variable = raVar.getVariable(); + if (variable.getValueKind().equals(ValueKind.Illegal)) { + continue; + } + + // Set the kind as illegal (should trigger an exception in most cases) + values.orig[i] = RAValue.create(new Variable(ValueKind.Illegal, variable.index)); + this.variable = variable; + return true; + } + + return false; + } + } + + /** + * Change kind for a variable that is being used as an input. + */ + class ChangeInputKindPhase extends ChangeKindPhase { + @Override + protected RAVInstruction.ValueArrayPair getTargetValueArrayPair(RAVInstruction.Op op) { + return op.uses; + } + } + + /** + * Change kind for a variable that is being used as an output. + */ + class ChangeOutputKindPhase extends ChangeKindPhase { + @Override + protected RAVInstruction.ValueArrayPair getTargetValueArrayPair(RAVInstruction.Op op) { + return op.dests; + } + } + + /** + * Modifies LIR instruction location in a way where + * an alive operand and destination or temporary use + * the same register. + *

+ * Finds the first instruction that satisfies having + * both alive operand and temp/output and changes it + * so one location is the same. + */ + abstract class ViolateAliveConstraint extends LIRPhase implements VerifierStateUser { + protected RegisterAllocationVerifierPhaseState state; + + @Override + public void setState(RegisterAllocationVerifierPhaseState state) { + this.state = state; + } + + class SetAliveRegProc implements ValueProcedure { + boolean first = true; + Value aliveValue; + + SetAliveRegProc(Value aliveValue) { + this.aliveValue = aliveValue; + } + + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + if (first) { + var registerValue = (RegisterValue) aliveValue; + var register = registerValue.getRegister(); + + first = false; + + return register.asValue(value.getValueKind()); + } + + return value; + } + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { + var lir = lirGenRes.getLIR(); + var instrMap = this.state.getInstructionMap(lirGenRes); + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + if (instruction instanceof StandardOp.LabelOp) { + continue; + } + + if (!instrMap.containsKey(instruction)) { + continue; + } + + var op = (RAVInstruction.Op) instrMap.get(instruction); + if (op.alive.count == 0) { + continue; + } + + var aliveValue = findAllocatedAliveValue(instruction); + var setAliveRegProc = new SetAliveRegProc(aliveValue); + changeLocationInTarget(instruction, setAliveRegProc); + if (!setAliveRegProc.first) { + return; + } + } + } + } + + protected abstract void changeLocationInTarget(LIRInstruction instruction, ValueProcedure setAliveRegProc); + + protected Value findAllocatedAliveValue(LIRInstruction instruction) { + final Value[] alive = {null}; + instruction.forEachAlive(new ValueProcedure() { + @Override + public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { + alive[0] = value; + return value; + } + }); + return alive[0]; + } + } + + /** + * Changes LIR instruction to use same register as alive and output. + */ + class ViolateAliveConstraintInDstPhase extends ViolateAliveConstraint { + @Override + protected void changeLocationInTarget(LIRInstruction instruction, ValueProcedure setAliveRegProc) { + instruction.forEachOutput(setAliveRegProc); + } + } + + /** + * Changes LIR instruction to use same register as alive and temporary. + */ + class ViolateAliveConstraintInTempPhase extends ViolateAliveConstraint { + @Override + protected void changeLocationInTarget(LIRInstruction instruction, ValueProcedure setAliveRegProc) { + instruction.forEachTemp(setAliveRegProc); + } + } + + abstract class ConflictPhase extends LIRPhase implements VerifierStateUser { + RegisterAllocationVerifierPhaseState state; + + @Override + public void setState(RegisterAllocationVerifierPhaseState state) { + this.state = state; + } + + RAVariable targetVariable; + RAVariable newVariable; + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { + var lir = lirGenRes.getLIR(); + var instrMap = this.state.getInstructionMap(lirGenRes); + var startBlock = lir.getControlFlowGraph().getStartBlock(); + + BasicBlock conflictBlock = getConflictUseBlock(lir); + assert conflictBlock != null; + + var variables = getVariablesFromVirtualMoves(lir, startBlock, instrMap); + + RAVariable targetVariable = getUsedVariableFromBlock(lir, conflictBlock, instrMap, variables); + assert targetVariable != null; + + var branchBlock = getConflictSourceBlock(lir, conflictBlock); + addVirtualMove(lir, branchBlock, instrMap, targetVariable, variables); + } + + /** + * Get block where a location with conflicted state will be used. + * + * @param lir LIR + * @return Conflicted use block + */ + protected abstract BasicBlock getConflictUseBlock(LIR lir); + + /** + * Get block where conflict will be created, + * by inserting a ValueMove instruction. + * + * @param lir LIR + * @param conflictBlock Block where conflict will be used + * @return Source of the conflict + */ + protected abstract BasicBlock getConflictSourceBlock(LIR lir, BasicBlock conflictBlock); + + protected Map getVariablesFromVirtualMoves(LIR lir, BasicBlock block, Map instrMap) { + var variables = new EconomicHashMap(); + var startInstructions = lir.getLIRforBlock(block); + for (var instruction : startInstructions) { + var op = (RAVInstruction.Op) instrMap.get(instruction); + if (op == null) { + continue; + } + + for (var move : op.getVirtualMoveList()) { + if (move.variableOrConstant.isVariable()) { + variables.put(move.variableOrConstant.asVariable(), move.location); + } + } + } + return variables; + } + + protected RAVariable getUsedVariableFromBlock(LIR lir, BasicBlock block, Map instrMap, Map variables) { + var endInstructions = lir.getLIRforBlock(block); + for (var instruction : endInstructions) { + var op = (RAVInstruction.Op) instrMap.get(instruction); + if (op == null) { + continue; + } + + for (int i = 0; i < op.uses.count; i++) { + var orig = op.uses.orig[i]; + if (!orig.isVariable()) { + continue; + } + + if (variables.containsKey(orig.asVariable())) { + return orig.asVariable(); + } + } + } + return null; + } + + /** + * Adds a conflict inducing value move to a block. + * + * @param lir LIR + * @param block Block where we are putting move to + * @param instrMap Pre allocation instruction map + * @param targetVariable Target variable we are creating conflict with + * @param variables Variables and their locations from previous steps + */ + protected void addVirtualMove(LIR lir, BasicBlock block, Map instrMap, RAVariable targetVariable, Map variables) { + var instructions = lir.getLIRforBlock(block); + + boolean first = true; + for (var instruction : instructions.reversed()) { + if (first) { + first = false; + continue; + } + + var op = (RAVInstruction.Op) instrMap.get(instruction); + if (op == null) { + continue; + } + + var newVar = new Variable(targetVariable.getVariable().getValueKind(), lir.numVariables() + 1); + op.addVirtualMove(new RAVInstruction.ValueMove(null, newVar, variables.get(targetVariable).getValue())); + + this.newVariable = (RAVariable) RAValue.create(newVar); + this.targetVariable = targetVariable; + + break; + } + } + } + + class DiamondConflictPhase extends ConflictPhase { + @Override + protected BasicBlock getConflictUseBlock(LIR lir) { + BasicBlock endBlock = null; + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + if (block.getSuccessorCount() == 0) { + endBlock = block; + break; + } + } + return endBlock; + } + + @Override + protected BasicBlock getConflictSourceBlock(LIR lir, BasicBlock conflictBlock) { + return lir.getControlFlowGraph().getStartBlock().getSuccessorAt(0); + } + } + + class LoopConflictPhase extends ConflictPhase { + @Override + protected BasicBlock getConflictUseBlock(LIR lir) { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + + if (block.isLoopHeader()) { + return block; + } + } + return null; + } + + @Override + protected BasicBlock getConflictSourceBlock(LIR lir, BasicBlock loopBlock) { + for (int i = 0; i < loopBlock.getSuccessorCount(); i++) { + var succ = loopBlock.getSuccessorAt(i); + for (int j = 0; j < succ.getSuccessorCount(); j++) { + if (succ.getSuccessorAt(j).equals(loopBlock)) { + return succ; + } + } + } + return null; + } + } + + @Override + protected LIRSuites createLIRSuites(OptionValues options) { + if (validSuites) { + return createLIRSuitesWithVerifier(options); + } + + return createModifiedVerifierLIRSuites(options); + } + + protected LIRSuites createLIRSuitesWithVerifier(OptionValues options) { + LIRSuites suites = super.createLIRSuites(options); + var stage = suites.getAllocationStage(); + + if (RegisterAllocationVerifierPhase.Options.EnableRAVerifier.getValue(options)) { + return suites; + } + + var sharedState = new RegisterAllocationVerifierPhaseState(options); + var preAllocRAVPhase = new PreRegisterAllocationPhase(sharedState); + var ravPhase = new RegisterAllocationVerifierPhase(sharedState); + + stage.prependPhase(preAllocRAVPhase); + + var it = stage.findPhase(RegisterAllocationPhase.class); + if (it == null) { + throw new IllegalStateException(); + } + + it.add(ravPhase); + + return suites; + } + + protected LIRSuites createModifiedVerifierLIRSuites(OptionValues options) { + LIRSuites suites = super.createLIRSuites(options); + var stage = suites.getAllocationStage(); + + if (RegisterAllocationVerifierPhase.Options.EnableRAVerifier.getValue(options)) { + var it = stage.findPhase(RegisterAllocationVerifierPhase.class); + if (it != null) { + var verifierPhase = (RegisterAllocationVerifierPhase) it.previous(); + + var sharedState = verifierPhase.getState(); + it.set(new RAVPhaseWrapper(sharedState)); + + if (phase instanceof VerifierStateUser stateUser) { + stateUser.setState(sharedState); + } + + it.add(phase); // Inserted before the verifier phase + return suites; + } + } + + var sharedState = new RegisterAllocationVerifierPhaseState(options); + var preAllocRAVPhase = new PreRegisterAllocationPhase(sharedState); + var ravPhase = new RAVPhaseWrapper(sharedState); + + stage.prependPhase(preAllocRAVPhase); + + var it = stage.findPhase(RegisterAllocationPhase.class); + if (it == null) { + throw new IllegalStateException(); + } + + if (phase instanceof VerifierStateUser stateUser) { + // This phase needs access to the verifier state + // in order to make changes that will create an + // exception. + stateUser.setState(sharedState); + } + + it.add(phase); // use modifier phase here + it.add(ravPhase); + + return suites; + } + + protected void compileModified(String name) { + validSuites = false; + compile(getResolvedJavaMethod(name), null); + validSuites = true; + } + + protected void assertException(Class expected) { + Assert.assertNotNull("No exception was thrown", exception); + Assert.assertTrue("Unexpected exception: " + exception, expected.isInstance(exception)); + } + + public static int simple(int a) { + return a + 1; + } + + public static int keep(int a) { + int b = 5 * a; // Make sure b is in different reg than a + return b + a; + } + + public static int diamond(int a, int b, int c, int d, int e) { + int x; + if (a > 0) { + x = b * e; + } else { + x = c + d + 2 * b; + } + + return x + a; + } + + public static int sum(int a, int b) { + int sum = 0; + for (int i = 0; i < a; i++) { + sum += i * b; + } + return sum; + } + + // toArray and arrayLengthProviderSnippet taken from ArrayLengthProviderTest + // to force dest and alive in an instruction + public static Object[] toArray(List list) { + return new Object[list.size()]; + } + + public static Object arrayLengthProviderSnippet(ArrayList list, boolean a) { + while (true) { + Object[] array = toArray(list); + if (array.length < 1) { + return null; + } + if (array[0] instanceof String || a) { + /* + * This code is outside of the loop. Accessing the array reqires a ValueProxyNode. + * When the simplification of the ArrayLengthNode replaces the length access with + * the ArrayList.size used to create the array, then the value needs to have a + * ValueProxyNode too. In addition, the two parts of the if-condition actually lead + * to two separate loop exits, with two separate proxy nodes. A ValuePhiNode is + * present originally for the array, and the array length simplification needs to + * create a new ValuePhiNode for the two newly introduced ValueProxyNode. + */ + if (array.length < 1) { + return null; + } + return array[0]; + } + } + } + + // Taken from EnumSwitchTest + // to find an instruction with both temp and alive + public static int aliveConstraintSnippet(Ex e) { + switch (e) { + case E0: + return 0; + case E1: + return 1; + case E2: + return 2; + case E3: + return 3; + default: + return -1; + } + } + + @Before + public void prepareTest() { + exception = null; + validSuites = true; + phase = null; + } + + @Test + public void testInvalidRegisterUsed() { + var disallowedRegPhase = new DisallowedRegisterPhase(); + phase = disallowedRegPhase; + + var methodName = "simple"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(InvalidRegisterUsedException.class); + var iruException = (InvalidRegisterUsedException) exception; + Assert.assertEquals(iruException.register, disallowedRegPhase.ignoredReg); // Used forbidden register + } + + @Test + public void testWrongVariableInState() { + var changeVariablePhase = new ChangeVariablePhase(); + phase = changeVariablePhase; + + var methodName = "simple"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(ValueNotInRegisterException.class); + + var vnrException = (ValueNotInRegisterException) exception; + Assert.assertEquals(changeVariablePhase.originalVariable, vnrException.variable); // Expected original variable + Assert.assertTrue(vnrException.state instanceof ValueAllocationState); + Assert.assertEquals(changeVariablePhase.newVariable, ((ValueAllocationState) vnrException.state).getRAValue()); // But new variable is there instead + } + + @Test + public void testUnknownLocation() { + var changeLocationPhase = new ForceUnknownStateInRegister(); + phase = changeLocationPhase; + + var methodName = "keep"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(ValueNotInRegisterException.class); + + var vnrException = (ValueNotInRegisterException) exception; + Assert.assertTrue(vnrException.state.isUnknown()); + Assert.assertEquals(vnrException.variable, changeLocationPhase.variable); + Assert.assertEquals(vnrException.location.getValue(), changeLocationPhase.oldRegister); + } + + @Test + public void testConflictedLoc() { + var diamondConflictPhase = new DiamondConflictPhase(); + phase = diamondConflictPhase; + + var methodName = "diamond"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(ValueNotInRegisterException.class); + var vnrException = (ValueNotInRegisterException) exception; + Assert.assertTrue(vnrException.state.isConflicted()); + + var confState = (ConflictedAllocationState) vnrException.state; + var conflitedStates = confState.getConflictedStates(); + Assert.assertEquals(2, conflitedStates.size()); + for (var state : conflitedStates) { + Assert.assertTrue(state.getRAValue().equals(diamondConflictPhase.newVariable) || state.getRAValue().equals(diamondConflictPhase.targetVariable)); + } + } + + @Test + public void testConflictInLoop() { + var loopConflictPhase = new LoopConflictPhase(); + phase = loopConflictPhase; + + var methodName = "sum"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(ValueNotInRegisterException.class); + var vnrException = (ValueNotInRegisterException) exception; + Assert.assertTrue(vnrException.state.isConflicted()); + + var confState = (ConflictedAllocationState) vnrException.state; + var conflitedStates = confState.getConflictedStates(); + Assert.assertEquals(2, conflitedStates.size()); + for (var state : conflitedStates) { + Assert.assertTrue(state.getRAValue().equals(loopConflictPhase.newVariable) || state.getRAValue().equals(loopConflictPhase.targetVariable)); + } + } + + + @Test + public void testAliveConstraintInDest() { + var violateAliveConstraintPhase = new ViolateAliveConstraintInDstPhase(); + phase = violateAliveConstraintPhase; + + var methodName = "arrayLengthProviderSnippet"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(AliveConstraintViolationException.class); + } + + @Test + public void testAliveConstraintInTemp() { + var violateAliveConstraintPhase = new ViolateAliveConstraintInTempPhase(); + phase = violateAliveConstraintPhase; + + var methodName = "aliveConstraintSnippet"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(AliveConstraintViolationException.class); + } + + @Test + public void testKindMatchAfterAlloc() { + var changeInputKindPhase = new ChangeInputKindPhase(); + phase = changeInputKindPhase; + + var methodName = "keep"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(KindsMismatchException.class); + var kmException = (KindsMismatchException) exception; + Assert.assertEquals(changeInputKindPhase.variable.index, kmException.value1.asVariable().getVariable().index); + Assert.assertEquals(changeInputKindPhase.variable.getValueKind(), kmException.value2.getValue().getValueKind()); + Assert.assertEquals(ValueKind.Illegal, kmException.value1.getValue().getValueKind()); + Assert.assertTrue(kmException.origVsCurr); + } + + @Test + public void testKindMatchInState() { + var changeInputKindPhase = new ChangeOutputKindPhase(); + phase = changeInputKindPhase; + + var methodName = "keep"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(KindsMismatchException.class); + var kmException = (KindsMismatchException) exception; + Assert.assertEquals(changeInputKindPhase.variable.index, kmException.value1.asVariable().getVariable().index); + Assert.assertEquals(changeInputKindPhase.variable.index, kmException.value2.asVariable().getVariable().index); + Assert.assertEquals(changeInputKindPhase.variable.getValueKind(), kmException.value1.getValue().getValueKind()); + Assert.assertEquals(ValueKind.Illegal, kmException.value2.getValue().getValueKind()); + Assert.assertFalse(kmException.origVsCurr); + } +} + +// Taken from EnumSwitchTest, shortened +enum Ex { + E0, + E1, + E2, + E3, +} From 13faefd44edf9f2f7e55261483375b744e2f528f Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 2 Mar 2026 23:09:18 +0100 Subject: [PATCH 057/112] Remove phi resolution option --- .../alloc/verifier/LabelNotResolvedError.java | 73 ------------------- .../verifier/MergedBlockVerifierState.java | 16 +--- .../lir/alloc/verifier/PhiResolution.java | 43 ----------- .../verifier/RegisterAllocationVerifier.java | 15 +--- .../RegisterAllocationVerifierPhase.java | 8 +- .../RegisterAllocationVerifierPhaseState.java | 2 - 6 files changed, 6 insertions(+), 151 deletions(-) delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java deleted file mode 100644 index b71ee43bf725..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/LabelNotResolvedError.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2026, 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.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; - -/** - * Could not resolve label variable locations - * and thus cannot proceed further, an error - * caused by the Verifier, non-recoverable. - */ -@SuppressWarnings("serial") -public class LabelNotResolvedError extends RAVError { - public BasicBlock block; - public RAVInstruction.Op label; - public PhiResolution resolution; - - public LabelNotResolvedError(BasicBlock block, RAVInstruction.Op label, PhiResolution resolution) { - super(LabelNotResolvedError.getErrorMessage(label)); - - this.block = block; - this.label = label; - this.resolution = resolution; - } - - static String getErrorMessage(RAVInstruction.Op label) { - StringBuilder labelStringBuilder = new StringBuilder("["); - StringBuilder unresolvedVariablesStringBuilder = new StringBuilder(); - for (int i = 0; i < label.dests.count; i++) { - var variable = label.dests.orig[i]; - var location = label.dests.curr[i]; - - labelStringBuilder.append(variable.toString()); - if (location != null) { - labelStringBuilder.append(" -> ").append(location); - } else { - labelStringBuilder.append(" -> ?"); - - unresolvedVariablesStringBuilder.append(variable); - unresolvedVariablesStringBuilder.append(", "); - } - - labelStringBuilder.append(", "); - } - - int unresLen = unresolvedVariablesStringBuilder.length(); - unresolvedVariablesStringBuilder.delete(unresLen - 2, unresLen); - - return "Could not resolve " + unresolvedVariablesStringBuilder + ": LABEL " + labelStringBuilder + "]"; - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index b88e37f7fb7c..135217737525 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -48,11 +48,6 @@ public class MergedBlockVerifierState { */ public MergedAllocationStateMap values; - /** - * Label variable resolution method used. - */ - protected PhiResolution phiResolution; - /** * Register allocation config we use to check if only * allocatable registers are the ones used. @@ -64,15 +59,13 @@ public class MergedBlockVerifierState { */ protected ConflictResolver conflictConstantResolver; - public MergedBlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, PhiResolution phiResolution, ConflictResolver constantConflictResolver) { + public MergedBlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver) { this.values = new MergedAllocationStateMap(block, registerAllocationConfig); - this.phiResolution = phiResolution; this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; } protected MergedBlockVerifierState(BasicBlock block, MergedBlockVerifierState other) { - this.phiResolution = other.phiResolution; this.registerAllocationConfig = other.registerAllocationConfig; this.conflictConstantResolver = other.conflictConstantResolver; this.values = new MergedAllocationStateMap(block, other.values); @@ -111,12 +104,7 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. if (curr == null) { if (op.isJump()) { - if (phiResolution == PhiResolution.ByAllocator || phiResolution == PhiResolution.FromUsageGlobal) { - // Variable has no usage, thus no location present. - continue; - } - - throw new LabelNotResolvedError(block, labelOp, phiResolution); + continue; } throw new MissingLocationError(op.lirInstruction, block, orig); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java deleted file mode 100644 index 235724af5483..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PhiResolution.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2026, 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.alloc.verifier; - -/** - * Label variable location resolution method. - */ -public enum PhiResolution { - /** - * By looking up variables first usage, - * and walking back to defining label - - * every variable at once. - */ - FromUsageGlobal, - /** - * Modify the allocator to keep - * label variable locations present - * after the allocation. - */ - ByAllocator -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index ad59b8520a96..e507f67356bf 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -68,12 +68,6 @@ public class RegisterAllocationVerifier { */ protected RegisterAllocationConfig registerAllocationConfig; - /** - * Resolution method used for determining - * label variable locations. - */ - protected PhiResolution phiResolution; - /** * Resolves locations for label variables by finding * their first usage and walking back to the defining @@ -86,7 +80,7 @@ public class RegisterAllocationVerifier { */ protected ConflictResolver constantMaterializationConflictResolver; - public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, PhiResolution phiResolution, RegisterAllocationConfig registerAllocationConfig) { + public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; this.registerAllocationConfig = registerAllocationConfig; @@ -97,7 +91,6 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b this.blockEntryStates = new BlockMap<>(cfg); this.blockStates = new BlockMap<>(cfg); - this.phiResolution = phiResolution; this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); } @@ -113,7 +106,7 @@ public void calculateEntryBlocks() { Queue> worklist = new ArrayDeque<>(); var startBlock = this.lir.getControlFlowGraph().getStartBlock(); - this.blockEntryStates.put(startBlock, new MergedBlockVerifierState(startBlock, registerAllocationConfig, phiResolution, constantMaterializationConflictResolver)); + this.blockEntryStates.put(startBlock, new MergedBlockVerifierState(startBlock, registerAllocationConfig, constantMaterializationConflictResolver)); worklist.add(this.lir.getControlFlowGraph().getStartBlock()); while (!worklist.isEmpty()) { @@ -206,9 +199,7 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { */ public void run() { this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); - if (this.phiResolution == PhiResolution.FromUsageGlobal) { - this.fromUsageResolverGlobal.resolvePhiFromUsage(); - } + this.fromUsageResolverGlobal.resolvePhiFromUsage(); this.calculateEntryBlocks(); this.verifyInstructionInputs(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 294952527831..4a967ff42722 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -62,12 +62,6 @@ public static class Options { @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) public static final OptionKey EnableRAVerifier = new OptionKey<>(false); - @Option(help = "Select which way you want to resolve phi arguments.", type = OptionType.Debug) - public static final EnumOptionKey RAPhiResolution = new EnumOptionKey<>(PhiResolution.FromUsage); - - @Option(help = "Should constants be moved to variables when needed", type = OptionType.Debug) - public static final OptionKey MoveConstants = new OptionKey<>(true); - @Option(help = "Substring necessary to be found for method to be verified", type = OptionType.Debug) public static final OptionKey RAFilter = new OptionKey<>(null); } @@ -95,7 +89,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo } var instructions = getVerifierInstructions(lirGenRes); - var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, this.state.phiResolution, context.registerAllocationConfig); + var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, context.registerAllocationConfig); // For timers for time spent in pre-alloc and verification phases look for these metric keys: // LIRPhaseTime_PreRegisterAllocationPhase & LIRPhaseTime_RegisterAllocationVerifierPhase diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java index f31f99b7aef4..56c7e27be812 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java @@ -38,7 +38,6 @@ * pertaining mostly to shared config and Verifier IR. */ public class RegisterAllocationVerifierPhaseState { - public PhiResolution phiResolution; public String filterStr; /** @@ -49,7 +48,6 @@ public class RegisterAllocationVerifierPhaseState { public RegisterAllocationVerifierPhaseState(OptionValues options) { this.verifierInstructions = new EconomicHashMap<>(Equivalence.IDENTITY); - this.phiResolution = RegisterAllocationVerifierPhase.Options.RAPhiResolution.getValue(options); this.filterStr = RegisterAllocationVerifierPhase.Options.RAFilter.getValue(options); } From aec27801479a616388169d3749937a1f2ec9003b Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 3 Mar 2026 17:04:29 +0100 Subject: [PATCH 058/112] Save values from ByteCodeFrame --- .../verifier/MergedBlockVerifierState.java | 45 ++++++++++--------- .../verifier/PreRegisterAllocationPhase.java | 11 ++++- .../lir/alloc/verifier/RAVInstruction.java | 12 +++-- .../RegisterAllocationVerifierPhase.java | 40 +++++++++++++---- 4 files changed, 74 insertions(+), 34 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 135217737525..780e8b80109e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -32,6 +32,7 @@ import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; @@ -104,6 +105,9 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. if (curr == null) { if (op.isJump()) { + // This can happen if a variable without a usage is passed in + // even when this variable acts as an alias to the next label + // there's no usage, so no location. continue; } @@ -237,7 +241,7 @@ public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstr checkTempKind(op, block); checkAliveConstraint(op, block); - checkStateJavaKinds(op, block); + // checkStateJavaKinds(op, block); checkOperandFlags(op.dests, op, block); checkOperandFlags(op.uses, op, block); @@ -308,40 +312,37 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock * @param block In which block is this operation */ protected void checkStateJavaKinds(RAVInstruction.Op op, BasicBlock block) { - int kindIdx = 0; - for (int i = 0; i < op.stateValues.count; i++) { - var orig = op.stateValues.curr[i]; - var curr = op.stateValues.curr[i]; - - JavaKind kind = null; - while (kindIdx < op.kinds.length) { - JavaKind target = op.kinds[kindIdx++]; - if (!JavaKind.Illegal.equals(target)) { - kind = target; - break; - } - // Illegal values are ignored when iterating over state values - // but kept in the kinds array so we need to skip them. - } + if (op.frameSlotKinds == null) { + return; + } - if (kind == null) { - break; // We no longer have a JavaKind for other values + for (int i = 0; i < op.frameSlotKinds.length; i++) { + var kind = op.frameSlotKinds[i]; + var orig = op.origFrameSlots[i]; + var curr = op.currFrameSlots[i]; + + if (!(orig instanceof AllocatableValue origAllocValue) || Value.ILLEGAL.equals(origAllocValue)) { + continue; } - var origLIRKind = orig.getLIRKind(); - var currLIRKind = curr.getLIRKind(); + var currAllocValue = (AllocatableValue) curr; + + var origLIRKind = origAllocValue.getValueKind(LIRKind.class); + var currLIRKind = currAllocValue.getValueKind(LIRKind.class); if (JavaKind.Object.equals(kind)) { if (!origLIRKind.isValue() && !currLIRKind.isValue()) { continue; } - throw new RAVException(orig.getValue() + " -> " + curr.getValue() + " not an object java kind when marked as a reference"); + throw new RAVException(origAllocValue + " -> " + currAllocValue + " not an object java kind when marked as a reference"); } else { if (origLIRKind.isValue() && currLIRKind.isValue()) { continue; } - throw new RAVException(orig.getValue() + " -> " + curr.getValue() + " is a reference when not marked as an object java kind"); + // Test: PointerTrackingTest + // has vstack marked as a reference, but long JavaKind. + throw new RAVException(origAllocValue + " -> " + currAllocValue + " is a reference when not marked as an object java kind"); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java index 5573ef09ef26..da66fb0fd3ee 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java @@ -126,10 +126,19 @@ public void doState(LIRInstruction instruction, LIRFrameState state) { return; } + var frameSlotKinds = state.topFrame.getSlotKinds(); + if (state.topFrame.values.length != frameSlotKinds.length) { + // Test: JVMCIInfopointErrorTest + // has defined slotKinds [boolean] + // but no values + return; + } + // Haven't found a case where there is multiple frame states on an instruction // so this will work, otherwise appending them would do the job in that case // if we could also get this information about VirtualObjects. - opRAVInstr.kinds = state.topFrame.getSlotKinds(); + opRAVInstr.origFrameSlots = state.topFrame.values; + opRAVInstr.frameSlotKinds = state.topFrame.getSlotKinds(); } }); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index d12beba3976b..159c5b518547 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -34,6 +34,7 @@ import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaValue; import jdk.vm.ci.meta.Value; import java.util.ArrayList; @@ -242,10 +243,15 @@ public static class Op extends Base { public ValueArrayPair alive; /** - * JavaKinds retrieved from LIRFrameState for - * the stateValues. + * Fields taken from BytecodeFrame stored + * in LIRFrame, we check these to make + * sure that whenever JavaKind is an Object, + * that LIRKind is a reference and if it is + * a primitive then make sure it is not a reference */ - public JavaKind[] kinds; + public JavaValue[] currFrameSlots; + public JavaValue[] origFrameSlots; + public JavaKind[] frameSlotKinds; /** * Pairs of values retrieved from LIRFrameState, diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java index 4a967ff42722..687d88338dbf 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java @@ -28,10 +28,11 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.InstructionStateProcedure; +import jdk.graal.compiler.lir.LIRFrameState; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.AllocationPhase; import jdk.graal.compiler.options.EnumOptionKey; @@ -40,16 +41,12 @@ import jdk.graal.compiler.options.OptionType; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.code.ValueUtil; -import jdk.vm.ci.meta.Value; import java.io.FileNotFoundException; import java.io.PrintStream; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -110,6 +107,10 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo VerifierPrinter.print(output, lir, instructions); } catch (FileNotFoundException ignored) { } + + // Keep original message with class path prefix and add debug path info + // to the end so it's easier to access. + new RAVException(e + ", see debug file " + debugPath, e); } throw e; @@ -151,7 +152,7 @@ protected BlockMap> getVerifierInstructions(LIRGenerat BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); for (var blockId : lir.getBlocks()) { BasicBlock block = lir.getBlockById(blockId); - var instructionList = new LinkedList(); + var instructionList = new ArrayList(); ArrayList instructions = lir.getLIRforBlock(block); for (var instruction : instructions) { @@ -174,10 +175,19 @@ protected BlockMap> getVerifierInstructions(LIRGenerat instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); instruction.forEachState(opRAVInstr.stateValues.copyCurrentProc); + instruction.forEachState(new InstructionStateProcedure() { + @Override + public void doState(LIRInstruction instruction, LIRFrameState state) { + if (state.topFrame == null) { + return; + } + + opRAVInstr.currFrameSlots = state.topFrame.values; + } + }); instructionList.add(opRAVInstr); var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); - if (!speculativeMoves.isEmpty()) { for (var speculativeMove : speculativeMoves) { if (presentInstructions.contains(speculativeMove.getLIRInstruction())) { @@ -186,9 +196,23 @@ protected BlockMap> getVerifierInstructions(LIRGenerat if (!speculativeMove.location.isVariable() && speculativeMove.variableOrConstant.isVariable()) { var variable = speculativeMove.variableOrConstant.asVariable(); - if (definedVariables.containsKey(variable)) { + var variableDefInstr = definedVariables.get(variable); + if (variableDefInstr == null) { continue; } + + if (variableDefInstr.lirInstruction instanceof StandardOp.LabelOp && variableDefInstr.lirInstruction == opRAVInstr.lirInstruction) { + for (int i = 0; i < opRAVInstr.dests.count; i++) { + var orig = opRAVInstr.dests.orig[i]; + if (!orig.isVariable() || opRAVInstr.dests.curr[i] != null) { + continue; + } + + opRAVInstr.dests.curr[i] = speculativeMove.location; + } + } + + continue; } instructionList.add(speculativeMove); From 0431f9c517b915397388a26b30ed189bb7695a37 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 3 Mar 2026 20:30:03 +0100 Subject: [PATCH 059/112] Handle allocated stack slot --- .../alloc/verifier/MergedBlockVerifierState.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 780e8b80109e..50c585bde23d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -111,6 +111,12 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. continue; } + // Verifying Stack Allocator: + // Here we also need to handle stateValues being null + // because sometimes a StackLockValue is used and after + // stack allocation it is no longer given to us when + // iterating. This also causes issues in the if statement + // below items are not shifted in curr array throw new MissingLocationError(op.lirInstruction, block, orig); } @@ -136,6 +142,14 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. continue; } + if (ValueUtil.isStackSlot(curr.getValue()) && LIRValueUtil.isVirtualStackSlot(orig.getValue())) { + // TestCase: IntegerDivRemCanonicalizationTest + // instruction r10|QWORD = STACKLEA slot: stack:80|ILLEGAL[*] in B0 + // had vstack:0, which is not mentioned in first label or elsewhere + // so symbol vstack:0 won't be found + continue; + } + if (state.isUnknown()) { throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } From 5fd14e08f3b602d8bdad74ca4e68d6a9b0adafec Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 4 Mar 2026 11:47:25 +0100 Subject: [PATCH 060/112] Wrap around allocators --- .../core/test/RegAllocVerifierTest.java | 205 +++----- .../verifier/FromUsageResolverGlobal.java | 5 +- .../verifier/MergedBlockVerifierState.java | 224 ++++----- .../verifier/PreRegisterAllocationPhase.java | 197 -------- .../lir/alloc/verifier/RAVInstruction.java | 30 +- .../compiler/lir/alloc/verifier/RAValue.java | 14 + .../alloc/verifier/RegAllocVerifierPhase.java | 438 ++++++++++++++++++ .../verifier/RegisterAllocationVerifier.java | 42 +- .../RegisterAllocationVerifierPhase.java | 272 ----------- .../RegisterAllocationVerifierPhaseState.java | 104 ----- .../compiler/lir/phases/AllocationStage.java | 34 +- 11 files changed, 677 insertions(+), 888 deletions(-) delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index 06bf61bcb599..881c7618dfe7 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -26,6 +26,7 @@ import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.StandardOp; @@ -36,18 +37,15 @@ import jdk.graal.compiler.lir.alloc.verifier.ConflictedAllocationState; import jdk.graal.compiler.lir.alloc.verifier.InvalidRegisterUsedException; import jdk.graal.compiler.lir.alloc.verifier.KindsMismatchException; -import jdk.graal.compiler.lir.alloc.verifier.PreRegisterAllocationPhase; import jdk.graal.compiler.lir.alloc.verifier.RAVException; import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; import jdk.graal.compiler.lir.alloc.verifier.RAValue; import jdk.graal.compiler.lir.alloc.verifier.RAVariable; -import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhase; -import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhaseState; +import jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase; import jdk.graal.compiler.lir.alloc.verifier.ValueAllocationState; import jdk.graal.compiler.lir.alloc.verifier.ValueNotInRegisterException; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.AllocationPhase; -import jdk.graal.compiler.lir.phases.LIRPhase; import jdk.graal.compiler.lir.phases.LIRSuites; import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.util.EconomicHashMap; @@ -63,7 +61,6 @@ import java.util.ArrayList; import java.util.EnumSet; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -82,22 +79,11 @@ public class RegAllocVerifierTest extends GraalCompilerTest { * Phase that causes RAVException to be thrown, * by modifying LIR or Verifier State. */ - LIRPhase phase; + RAVPhaseWrapper phase; - interface VerifierStateUser { - /** - * Set the currently used verification state to phase that - * implements this interface in-order for it to facilitate - * a change that will make the verifier find an exception. - * - * @param state Verifier state shared across it's phases - */ - void setState(RegisterAllocationVerifierPhaseState state); - } - - class RAVPhaseWrapper extends RegisterAllocationVerifierPhase { - RAVPhaseWrapper(RegisterAllocationVerifierPhaseState state) { - super(state); + class RAVPhaseWrapper extends RegAllocVerifierPhase { + public RAVPhaseWrapper() { + super(null, null); } @Override @@ -115,21 +101,18 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo * to cause a ValueNotInLocationException with old variable being * in said place. */ - class ChangeVariablePhase extends LIRPhase implements VerifierStateUser { + class ChangeVariablePhase extends RAVPhaseWrapper { protected RAVariable originalVariable; protected RAVariable newVariable; - protected RegisterAllocationVerifierPhaseState state; @Override - public void setState(RegisterAllocationVerifierPhaseState state) { - this.state = state; + protected Map saveInstructionsPreAlloc(LIR lir) { + var result = super.saveInstructionsPreAlloc(lir); + modifyLIR(lir, result); + return result; } - @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { - var lir = lirGenRes.getLIR(); - // var instrMap = this.state.getInstructionMap(lirGenRes); - var instrMap = lirGenRes.getVIR(); + protected void modifyLIR(LIR lir, Map instrMap) { for (var blockId : lir.getBlocks()) { var block = lir.getBlockById(blockId); var instructions = lir.getLIRforBlock(block); @@ -139,21 +122,21 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo } var virInstr = instrMap.get(instruction); - if (virInstr instanceof RAVInstruction.Op op && changeVariable(lirGenRes, op)) { - return; + if (virInstr instanceof RAVInstruction.Op op && changeVariable(lir, op)) { + break; } } } } - protected boolean changeVariable(LIRGenerationResult lirGenRes, RAVInstruction.Op op) { + protected boolean changeVariable(LIR lir, RAVInstruction.Op op) { for (int i = 0; i < op.dests.count; i++) { if (!op.dests.orig[i].isVariable()) { continue; } var variable = op.dests.orig[i].asVariable(); - var newVariable = createNewVariable(lirGenRes, variable); + var newVariable = createNewVariable(lir, variable); op.dests.orig[i] = newVariable; @@ -166,8 +149,8 @@ protected boolean changeVariable(LIRGenerationResult lirGenRes, RAVInstruction.O return false; } - protected RAVariable createNewVariable(LIRGenerationResult lirGenRes, RAVariable oldVariable) { - var newVariable = new Variable(oldVariable.getValue().getValueKind(), lirGenRes.getLIR().numVariables() + 1); + protected RAVariable createNewVariable(LIR lir, RAVariable oldVariable) { + var newVariable = new Variable(oldVariable.getValue().getValueKind(), lir.numVariables() + 1); return (RAVariable) RAValue.create(newVariable); } } @@ -177,15 +160,21 @@ protected RAVariable createNewVariable(LIRGenerationResult lirGenRes, RAVariable * registers to be used for allocation, and we want the verifier to * detect usage of said register. */ - class DisallowedRegisterPhase extends LIRPhase { + class DisallowedRegisterPhase extends RAVPhaseWrapper { protected Register ignoredReg; + protected RegisterAllocationConfig regAllocConfig; @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { - var lir = lirGenRes.getLIR(); - + protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext context) { var restrictedTo = getAllocationRestrictedToArray(lir, context.registerAllocationConfig); - context.registerAllocationConfig = new RegisterAllocationConfig(context.registerAllocationConfig.getRegisterConfig(), restrictedTo); + regAllocConfig = new RegisterAllocationConfig(context.registerAllocationConfig.getRegisterConfig(), restrictedTo); + + return super.getVerifierInstructions(lir, preallocMap, context); + } + + @Override + protected RegisterAllocationConfig getRegisterAllocationConfig(AllocationContext context) { + return regAllocConfig; } protected void modifyAllocatableRegisterList(LIR lir, List registerList) { @@ -250,31 +239,28 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet implements VerifierStateUser { + class ForceUnknownStateInRegister extends RAVPhaseWrapper { protected RAVariable variable; protected RegisterValue oldRegister = null; protected RegisterValue newRegister; - protected RegisterAllocationVerifierPhaseState state; protected int idx = 0; + protected Map regUsage; + ForceUnknownStateInRegister() { + super(); this.regUsage = new EconomicHashMap<>(); } - @Override - public void setState(RegisterAllocationVerifierPhaseState state) { - this.state = state; - } - - protected Map regUsage; @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { - var lir = lirGenRes.getLIR(); - var instrMap = this.state.getInstructionMap(lirGenRes); + protected BlockMap> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { + modifyLIR(lir, instrMap, context); + return super.getVerifierInstructions(lir, instrMap, context); + } + protected void modifyLIR(LIR lir, Map instrMap, AllocationContext context) { var replacementReg = getUnusedAllowedRegister(lir, context.registerAllocationConfig); - for (var blockId : lir.getBlocks()) { var block = lir.getBlockById(blockId); var instructions = lir.getLIRforBlock(block); @@ -286,7 +272,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo return; } - if (handleVirtualMoves(op, getUnusedAllowedRegister(lir, context.registerAllocationConfig))) { + if (handleVirtualMoves(op, replacementReg)) { return; } } @@ -389,19 +375,12 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet implements VerifierStateUser { - protected RegisterAllocationVerifierPhaseState state; + abstract class ChangeKindPhase extends RAVPhaseWrapper { protected Variable variable; @Override - public void setState(RegisterAllocationVerifierPhaseState state) { - this.state = state; - } - - @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { - var lir = lirGenRes.getLIR(); - var instrMap = this.state.getInstructionMap(lirGenRes); + protected Map saveInstructionsPreAlloc(LIR lir) { + var instrMap = super.saveInstructionsPreAlloc(lir); for (var blockId : lir.getBlocks()) { var block = lir.getBlockById(blockId); var instructions = lir.getLIRforBlock(block); @@ -411,11 +390,17 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo } var op = (RAVInstruction.Op) instrMap.get(instruction); + if (op == null) { + continue; + } + if (changeVariableKind(getTargetValueArrayPair(op))) { - return; + return instrMap; } } } + + return instrMap; } protected abstract RAVInstruction.ValueArrayPair getTargetValueArrayPair(RAVInstruction.Op op); @@ -472,14 +457,7 @@ protected RAVInstruction.ValueArrayPair getTargetValueArrayPair(RAVInstruction.O * both alive operand and temp/output and changes it * so one location is the same. */ - abstract class ViolateAliveConstraint extends LIRPhase implements VerifierStateUser { - protected RegisterAllocationVerifierPhaseState state; - - @Override - public void setState(RegisterAllocationVerifierPhaseState state) { - this.state = state; - } - + abstract class ViolateAliveConstraint extends RAVPhaseWrapper { class SetAliveRegProc implements ValueProcedure { boolean first = true; Value aliveValue; @@ -504,9 +482,7 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { for (var blockId : lir.getBlocks()) { var block = lir.getBlockById(blockId); var instructions = lir.getLIRforBlock(block); @@ -528,10 +504,12 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo var setAliveRegProc = new SetAliveRegProc(aliveValue); changeLocationInTarget(instruction, setAliveRegProc); if (!setAliveRegProc.first) { - return; + return super.getVerifierInstructions(lir, instrMap, context); } } } + + return super.getVerifierInstructions(lir, instrMap, context); } protected abstract void changeLocationInTarget(LIRInstruction instruction, ValueProcedure setAliveRegProc); @@ -569,21 +547,14 @@ protected void changeLocationInTarget(LIRInstruction instruction, ValueProcedure } } - abstract class ConflictPhase extends LIRPhase implements VerifierStateUser { - RegisterAllocationVerifierPhaseState state; - - @Override - public void setState(RegisterAllocationVerifierPhaseState state) { - this.state = state; - } - + abstract class ConflictPhase extends RAVPhaseWrapper { RAVariable targetVariable; RAVariable newVariable; @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { - var lir = lirGenRes.getLIR(); - var instrMap = this.state.getInstructionMap(lirGenRes); + protected Map saveInstructionsPreAlloc(LIR lir) { + var instrMap = super.saveInstructionsPreAlloc(lir); + var startBlock = lir.getControlFlowGraph().getStartBlock(); BasicBlock conflictBlock = getConflictUseBlock(lir); @@ -596,6 +567,8 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo var branchBlock = getConflictSourceBlock(lir, conflictBlock); addVirtualMove(lir, branchBlock, instrMap, targetVariable, variables); + + return instrMap; } /** @@ -610,7 +583,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo * Get block where conflict will be created, * by inserting a ValueMove instruction. * - * @param lir LIR + * @param lir LIR * @param conflictBlock Block where conflict will be used * @return Source of the conflict */ @@ -751,22 +724,15 @@ protected LIRSuites createLIRSuitesWithVerifier(OptionValues options) { LIRSuites suites = super.createLIRSuites(options); var stage = suites.getAllocationStage(); - if (RegisterAllocationVerifierPhase.Options.EnableRAVerifier.getValue(options)) { + if (RegAllocVerifierPhase.Options.EnableRAVerifier.getValue(options)) { return suites; } - var sharedState = new RegisterAllocationVerifierPhaseState(options); - var preAllocRAVPhase = new PreRegisterAllocationPhase(sharedState); - var ravPhase = new RegisterAllocationVerifierPhase(sharedState); - - stage.prependPhase(preAllocRAVPhase); - var it = stage.findPhase(RegisterAllocationPhase.class); - if (it == null) { - throw new IllegalStateException(); - } + assert it != null; - it.add(ravPhase); + var allocator = (RegisterAllocationPhase) it.previous(); + it.set(new RegAllocVerifierPhase(allocator, null)); return suites; } @@ -775,43 +741,17 @@ protected LIRSuites createModifiedVerifierLIRSuites(OptionValues options) { LIRSuites suites = super.createLIRSuites(options); var stage = suites.getAllocationStage(); - if (RegisterAllocationVerifierPhase.Options.EnableRAVerifier.getValue(options)) { - var it = stage.findPhase(RegisterAllocationVerifierPhase.class); - if (it != null) { - var verifierPhase = (RegisterAllocationVerifierPhase) it.previous(); - - var sharedState = verifierPhase.getState(); - it.set(new RAVPhaseWrapper(sharedState)); - - if (phase instanceof VerifierStateUser stateUser) { - stateUser.setState(sharedState); - } - - it.add(phase); // Inserted before the verifier phase - return suites; - } - } - - var sharedState = new RegisterAllocationVerifierPhaseState(options); - var preAllocRAVPhase = new PreRegisterAllocationPhase(sharedState); - var ravPhase = new RAVPhaseWrapper(sharedState); - - stage.prependPhase(preAllocRAVPhase); - var it = stage.findPhase(RegisterAllocationPhase.class); - if (it == null) { - throw new IllegalStateException(); - } + Assert.assertNotNull(it); - if (phase instanceof VerifierStateUser stateUser) { - // This phase needs access to the verifier state - // in order to make changes that will create an - // exception. - stateUser.setState(sharedState); + var allocator = (RegisterAllocationPhase) it.previous(); + if (allocator instanceof RegAllocVerifierPhase rav) { + phase.setAllocator(rav.getAllocator()); + } else { + phase.setAllocator(allocator); } - it.add(phase); // use modifier phase here - it.add(ravPhase); + it.set(phase); return suites; } @@ -1014,7 +954,6 @@ public void testConflictInLoop() { } } - @Test public void testAliveConstraintInDest() { var violateAliveConstraintPhase = new ViolateAliveConstraintInDstPhase(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 3458ea1d8baf..4f9204c1663a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -209,7 +209,10 @@ protected void initializeUsages() { handleUsages(op.uses, op, block); handleUsages(op.alive, op, block); - handleUsages(op.stateValues, op, block); + + if (op.hasCompleteState()) { + handleUsages(op.stateValues, op, block); + } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java index 50c585bde23d..1cd654c2d2c9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java @@ -31,9 +31,11 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.vm.ci.code.StackLockValue; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaValue; import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; @@ -86,114 +88,120 @@ public boolean meetWith(MergedBlockVerifierState other) { return this.values.mergeWith(other.getValues()); } + protected void checkStateValues(RAVInstruction.Op op, BasicBlock block) { + if (!op.hasCompleteState()) { + // Some values are null after allocation because of stack slot allocator + // because it is skipped when iteration (StackLockValue). + return; + } + + checkInputs(op.stateValues, op, block); + } + /** * Verify the correspondence of original variables used in instructions * are stored in the state of current locations. * - * @param values Array of pairs of current location and original variable. - * @param op Operation this input array of values belongs to - * @param block Block this operation is in - * @param labelOp Label of the successor block, in-case resolution was incomplete. + * @param values Array of pairs of current location and original variable. + * @param op Operation this input array of values belongs to + * @param block Block this operation is in */ - protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block, RAVInstruction.Op labelOp) { + protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { // Check that incoming values are not unknown or conflicted - these only matter if used for (int idx = 0; idx < values.count; idx++) { - var orig = values.orig[idx]; - var curr = values.curr[idx]; + checkOperand(values.orig[idx], values.curr[idx], op, block); + } + } - assert orig != null; + protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op, BasicBlock block) { + assert orig != null; - if (curr == null) { - if (op.isJump()) { - // This can happen if a variable without a usage is passed in - // even when this variable acts as an alias to the next label - // there's no usage, so no location. - continue; - } - - // Verifying Stack Allocator: - // Here we also need to handle stateValues being null - // because sometimes a StackLockValue is used and after - // stack allocation it is no longer given to us when - // iterating. This also causes issues in the if statement - // below items are not shifted in curr array - throw new MissingLocationError(op.lirInstruction, block, orig); + if (curr == null) { + if (op.isJump()) { + // This can happen if a variable without a usage is passed in + // even when this variable acts as an alias to the next label + // there's no usage, so no location. + return; } - if (!kindsEqual(orig, curr)) { - if (!op.isJump()) { - throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); - } + // Verifying Stack Allocator: + // Here we also need to handle stateValues being null + // because sometimes a StackLockValue is used and after + // stack allocation it is no longer given to us when + // iterating. This also causes issues in the if statement + // below items are not shifted in curr array + throw new MissingLocationError(op.lirInstruction, block, orig); + } - // Skip when jump due to this case: - // "rdx|QWORD[*] = MOVE input: rdx|QWORD[.+] moveKind: QWORD" - // this move is inserted by the allocator and changes type - // of rdx from [.+] (compressed reference) (same as original variable) to [*] (invalid reference) + if (!kindsEqual(orig, curr)) { + if (!op.isJump()) { + throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); } - AllocationState state = this.values.get(curr); - if (orig.equals(curr)) { - // For these cases we do not consider checking state taking the original - // register as a symbol, because there's too many cases when this does - // not work, for example RETURN with rax tends to contain the actual - // generated variable instead of rax symbol, or NEAR_FOREIGN_CALL - // keeps its own registers before and after allocation, but those - // can also contain different variable symbols. - continue; - } + // Skip when jump due to this case: + // "rdx|QWORD[*] = MOVE input: rdx|QWORD[.+] moveKind: QWORD" + // this move is inserted by the allocator and changes type + // of rdx from [.+] (compressed reference) (same as original variable) to [*] (invalid reference) + } - if (ValueUtil.isStackSlot(curr.getValue()) && LIRValueUtil.isVirtualStackSlot(orig.getValue())) { - // TestCase: IntegerDivRemCanonicalizationTest - // instruction r10|QWORD = STACKLEA slot: stack:80|ILLEGAL[*] in B0 - // had vstack:0, which is not mentioned in first label or elsewhere - // so symbol vstack:0 won't be found - continue; - } + AllocationState state = this.values.get(curr); + if (orig.equals(curr)) { + // For these cases we do not consider checking state taking the original + // register as a symbol, because there's too many cases when this does + // not work, for example RETURN with rax tends to contain the actual + // generated variable instead of rax symbol, or NEAR_FOREIGN_CALL + // keeps its own registers before and after allocation, but those + // can also contain different variable symbols. + return; + } - if (state.isUnknown()) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + if (ValueUtil.isStackSlot(curr.getValue()) && LIRValueUtil.isVirtualStackSlot(orig.getValue())) { + // TestCase: IntegerDivRemCanonicalizationTest + // instruction r10|QWORD = STACKLEA slot: stack:80|ILLEGAL[*] in B0 + // had vstack:0, which is not mentioned in first label or elsewhere + // so symbol vstack:0 won't be found + return; + } + + if (state.isUnknown()) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + if (state.isConflicted()) { + if (orig.isVariable()) { + var variable = orig.asVariable(); + var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); + if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { + this.values.put(curr, resolvedState); + return; + } } - if (state.isConflicted()) { - if (orig.isVariable()) { + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + } + + if (state instanceof ValueAllocationState valAllocState) { + if (!valAllocState.value.equals(orig)) { + if (LIRValueUtil.isConstantValue(valAllocState.value.getValue()) && orig.isVariable()) { var variable = orig.asVariable(); - var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); + var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { this.values.put(curr, resolvedState); - continue; + return; } } throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } - if (state instanceof ValueAllocationState valAllocState) { - if (!kindsEqualFromState(orig, valAllocState.value)) { - throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); - } - - if (!valAllocState.value.equals(orig)) { - if (LIRValueUtil.isCast(orig.getValue()) && valAllocState.value.getValue().equals(LIRValueUtil.uncast(orig.getValue()))) { - continue; // They aren't equal here because of the CastValue, so if they are equal afterwards, we skip next part. - } - - if (LIRValueUtil.isConstantValue(valAllocState.value.getValue())) { - var variable = orig.asVariable(); - var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); - if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { - this.values.put(curr, resolvedState); - continue; - } - } - - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); - } - - continue; + if (!kindsEqualFromState(orig, valAllocState.value)) { + throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); } - throw GraalError.shouldNotReachHere("Invalid state " + state); + return; } + + throw GraalError.shouldNotReachHere("Invalid state " + state); } /** @@ -245,17 +253,15 @@ protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { * * @param instruction Instruction we are checking * @param block Block where it is located - * @param labelOp Label of the successor block, in-case resolution failed */ - public void check(RAVInstruction.Base instruction, BasicBlock block, RAVInstruction.Op labelOp) { + public void check(RAVInstruction.Base instruction, BasicBlock block) { if (instruction instanceof RAVInstruction.Op op) { - checkInputs(op.uses, op, block, labelOp); - checkInputs(op.alive, op, block, labelOp); - checkInputs(op.stateValues, op, block, labelOp); + checkInputs(op.uses, op, block); + checkInputs(op.alive, op, block); + checkStateValues(op, block); checkTempKind(op, block); checkAliveConstraint(op, block); - // checkStateJavaKinds(op, block); checkOperandFlags(op.dests, op, block); checkOperandFlags(op.uses, op, block); @@ -318,49 +324,6 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock } } - /** - * Checks values from frame state to see if ones marked as Object JavaKind, - * are a reference in LIRKind. - * - * @param op Operation which we are checking state values for - * @param block In which block is this operation - */ - protected void checkStateJavaKinds(RAVInstruction.Op op, BasicBlock block) { - if (op.frameSlotKinds == null) { - return; - } - - for (int i = 0; i < op.frameSlotKinds.length; i++) { - var kind = op.frameSlotKinds[i]; - var orig = op.origFrameSlots[i]; - var curr = op.currFrameSlots[i]; - - if (!(orig instanceof AllocatableValue origAllocValue) || Value.ILLEGAL.equals(origAllocValue)) { - continue; - } - - var currAllocValue = (AllocatableValue) curr; - - var origLIRKind = origAllocValue.getValueKind(LIRKind.class); - var currLIRKind = currAllocValue.getValueKind(LIRKind.class); - if (JavaKind.Object.equals(kind)) { - if (!origLIRKind.isValue() && !currLIRKind.isValue()) { - continue; - } - - throw new RAVException(origAllocValue + " -> " + currAllocValue + " not an object java kind when marked as a reference"); - } else { - if (origLIRKind.isValue() && currLIRKind.isValue()) { - continue; - } - - // Test: PointerTrackingTest - // has vstack marked as a reference, but long JavaKind. - throw new RAVException(origAllocValue + " -> " + currAllocValue + " is a reference when not marked as an object java kind"); - } - } - } - /** * Make sure concrete current locations changed by the allocator * are not violating set of LIRInstruction.OperandFlag flags, @@ -432,10 +395,12 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { assert op.dests.orig[i] != null; - if (op.dests.curr[i] == null) { - continue; + if (op.dests.curr[i] == null && op.isLabel()) { + continue; // Unused label variable } + assert op.dests.curr[i] != null; + RAValue location = op.dests.curr[i]; RAValue variable = op.dests.orig[i]; @@ -475,7 +440,6 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { * @param references List of references deemed as live by the GC */ protected void updateWithSafePoint(List references) { - List toRemove = new ArrayList<>(); for (var entry : this.values.internalMap.entrySet()) { var state = entry.getValue(); if (state.isUnknown() || state.isConflicted()) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java deleted file mode 100644 index da66fb0fd3ee..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/PreRegisterAllocationPhase.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2026, 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.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.InstructionStateProcedure; -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.StandardOp; -import jdk.graal.compiler.lir.gen.LIRGenerationResult; -import jdk.graal.compiler.lir.phases.AllocationPhase; -import jdk.graal.compiler.util.EconomicHashMap; -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.code.ValueUtil; -import org.graalvm.collections.Equivalence; - -import java.util.ArrayList; -import java.util.Map; - -/** - * Pre-register allocation phase that needs to save information - * about variables/constants used in LIR instructions. - */ -public class PreRegisterAllocationPhase extends AllocationPhase { - /** - * Shared state with the verification phase. - */ - protected RegisterAllocationVerifierPhaseState state; - - public PreRegisterAllocationPhase(RegisterAllocationVerifierPhaseState state) { - this.state = state; - } - - /** - * Process every block and every instruction to save variables used in them for the verification procedure. - * - * @param target Machine architecture - * @param lirGenRes LIR generaration result of a method - * @param context Allocation context, used for RegisterAllocationConfig - */ - @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - if (!state.shouldBeVerified(lirGenRes)) { - return; - } - - var preallocMap = state.createInstructionMap(lirGenRes); - Map preallocMap = new EconomicHashMap<>(Equivalence.IDENTITY); - LIR lir = lirGenRes.getLIR(); - - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - ArrayList instructions = lir.getLIRforBlock(block); - - RAVInstruction.Base previousInstr = null; - for (var instruction : instructions) { - if (this.isVirtualMove(instruction)) { - // Virtual moves (variable = MOV real register) are going to be removed by the allocator, - // but we still need the information about which variables are associated to which real - // registers, and so we store them. They are generally associated to other instructions - // that's why we append them here to the previous instruction (for example Label or Foreign Call) - // use these, if this instruction was deleted in the allocator, then they will be missing too. - assert previousInstr != null; - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var location = valueMov.getInput(); - var variable = LIRValueUtil.asVariable(valueMov.getResult()); - - var virtualMove = new RAVInstruction.ValueMove(instruction, variable, location); - previousInstr.addVirtualMove(virtualMove); - continue; // No need to store virtual move here, it is stored into previous instruction. - } - - boolean speculative = false; - if (this.isSpeculativeMove(instruction)) { - speculative = true; - // Speculative moves are in form ry = MOVE vx, which could be removed if variable - // ends up being allocated to the same register as ry. If it was removed - // we need to re-add it because it holds important information about where value of - // this variable is placed - for label resolution after the label. - assert previousInstr != null; - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var variable = LIRValueUtil.asVariable(valueMov.getInput()); - var register = valueMov.getResult(); - - var virtualMove = new RAVInstruction.ValueMove(instruction, variable, register); - previousInstr.addSpeculativeMove(virtualMove); - } - - var opRAVInstr = new RAVInstruction.Op(instruction); - - instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); - instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); - instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); - instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); - instruction.forEachState(opRAVInstr.stateValues.copyOriginalProc); - instruction.forEachState(new InstructionStateProcedure() { - @Override - public void doState(LIRInstruction instruction, LIRFrameState state) { - if (state.topFrame == null) { - return; - } - - var frameSlotKinds = state.topFrame.getSlotKinds(); - if (state.topFrame.values.length != frameSlotKinds.length) { - // Test: JVMCIInfopointErrorTest - // has defined slotKinds [boolean] - // but no values - return; - } - - // Haven't found a case where there is multiple frame states on an instruction - // so this will work, otherwise appending them would do the job in that case - // if we could also get this information about VirtualObjects. - opRAVInstr.origFrameSlots = state.topFrame.values; - opRAVInstr.frameSlotKinds = state.topFrame.getSlotKinds(); - } - }); - - preallocMap.put(instruction, opRAVInstr); - - if (!speculative) { - previousInstr = opRAVInstr; - } - } - } - } - - /** - * Determines if instruction is a virtual move, a virtual move is - * a move instruction that moves a real register value into a variable, - * which is something that will always get removed from the final allocated - * IR. - *

- * This information is important to the verification process and needs to - * be part of the Verifier IR. - *

- * - * @param instruction LIR instruction we are looking at - * @return true, if instruction is a virtual move, otherwise false - */ - protected boolean isVirtualMove(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - return false; - } - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var input = valueMov.getInput(); - return (ValueUtil.isRegister(input) || LIRValueUtil.isStackSlotValue(input)) && LIRValueUtil.isVariable(valueMov.getResult()); - } - - /** - * Determines if a move is speculative - it could potentially be - * removed, but hold important information to the verification process. - *

- * For example, this happens for a move between two variables and after - * allocation locations are equal, making the move redundant. - *

- * - * @param instruction LIR instruction we are looking at - * @return true, if instruction is a speculative move, otherwise false - */ - protected boolean isSpeculativeMove(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - return false; - } - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var result = valueMov.getResult(); // Result could be variable or register - return (ValueUtil.isRegister(result) || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 159c5b518547..3c7252ef1101 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -31,6 +31,7 @@ import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.aarch64.AArch64Call; import jdk.graal.compiler.lir.amd64.AMD64Call; +import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.JavaKind; @@ -242,17 +243,6 @@ public static class Op extends Base { */ public ValueArrayPair alive; - /** - * Fields taken from BytecodeFrame stored - * in LIRFrame, we check these to make - * sure that whenever JavaKind is an Object, - * that LIRKind is a reference and if it is - * a primitive then make sure it is not a reference - */ - public JavaValue[] currFrameSlots; - public JavaValue[] origFrameSlots; - public JavaKind[] frameSlotKinds; - /** * Pairs of values retrieved from LIRFrameState, * verified same as any other input to make @@ -328,6 +318,24 @@ public boolean isSafePoint() { return lirInstruction instanceof AMD64HotSpotSafepointOp; } + /** + * Check if stateValues have null values, if so + * the state is not complete. This happens because + * iterating over certain values in LIRFrameState is + * ignored because they are a concrete stack slot and + * not a virtual one (StackLockValue). + * + * @return true, if complete - non-null values after allocation + */ + public boolean hasCompleteState() { + for (int i = 0; i < stateValues.count; i++) { + if (stateValues.curr[i] == null) { + return false; + } + } + return true; + } + @Override public String toString() { return this.dests.toString() + " = Op " + this.uses.toString() + " " + this.alive.toString() + " " + this.temp.toString(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index dc2f3e02d2ae..1ff31a78e981 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -26,6 +26,7 @@ import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; @@ -98,6 +99,11 @@ public int hashCode() { return LIRValueUtil.asVirtualStackSlot(this.value).getId(); } + if (ValueUtil.isStackSlot(this.value)) { + var stackSlot = ValueUtil.asStackSlot(this.value); + return stackSlot.getRawOffset(); + } + return this.value.hashCode(); } @@ -115,6 +121,10 @@ public boolean equals(Object other) { return LIRValueUtil.asVirtualStackSlot(this.value).getId() == LIRValueUtil.asVirtualStackSlot(otherValueWrap.value).getId(); } + if (ValueUtil.isStackSlot(this.value) && otherValueWrap.value.equals(this.value)) { + return ValueUtil.asStackSlot(this.value).getRawOffset() == ValueUtil.asStackSlot(otherValueWrap.value).getRawOffset(); + } + return this.value.equals(otherValueWrap.value); } @@ -127,6 +137,10 @@ public String toString() { return "vstack:" + LIRValueUtil.asVirtualStackSlot(this.value).getId(); } + if (ValueUtil.isStackSlot(this.value)) { + return "stack:" + ValueUtil.asStackSlot(this.value).getRawOffset(); + } + return value.toString(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java new file mode 100644 index 000000000000..166a6e3d0768 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.debug.DebugCloseable; +import jdk.graal.compiler.debug.DebugContext; +import jdk.graal.compiler.debug.TimerKey; +import jdk.graal.compiler.lir.ConstantValue; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.alloc.RegisterAllocationPhase; +import jdk.graal.compiler.lir.gen.LIRGenerationResult; +import jdk.graal.compiler.lir.phases.LIRPhase; +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionKey; +import jdk.graal.compiler.options.OptionType; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.code.ValueUtil; +import org.graalvm.collections.Equivalence; + +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Verification phase for Register Allocation, wraps around + * the actual allocator and validates that order of spills, reloads + * and moves is correct and that variables before allocations + * are actually stored in concrete locations chosen by the allocator. + *

+ * Needs to extend RegisterAllocationPhase to not throw an exception. + */ +public class RegAllocVerifierPhase extends RegisterAllocationPhase { + public static class Options { + @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) + public static final OptionKey EnableRAVerifier = new OptionKey<>(false); + } + + protected RegisterAllocationPhase allocator; + protected LIRPhase stackSlotAllocator; + + private static final TimerKey PreallocTimer = DebugContext.timer("RAV_PreAlloc"); + private static final TimerKey VerifierTimer = DebugContext.timer("RAV_Verification"); + + public RegAllocVerifierPhase(RegisterAllocationPhase allocator, LIRPhase stackSlotAllocator) { + this.allocator = allocator; + this.stackSlotAllocator = stackSlotAllocator; + } + + /** + * Get allocator that is being verified + * + * @return Register allocator + */ + public RegisterAllocationPhase getAllocator() { + return allocator; + } + + /** + * Set allocator to verify, used by tests. + * + * @param allocator Register allocator + */ + public void setAllocator(RegisterAllocationPhase allocator) { + this.allocator = allocator; + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + assert allocator != null : "No register allocator present for verification"; + + var lir = lirGenRes.getLIR(); + + Map preAllocMap; + try (DebugCloseable t = PreallocTimer.start(lir.getDebug())) { + preAllocMap = saveInstructionsPreAlloc(lir); + } + + allocator.apply(target, lirGenRes, context); + if (stackSlotAllocator != null) { + stackSlotAllocator.apply(target, lirGenRes, context); + } + + try (final DebugCloseable t = VerifierTimer.start(lir.getDebug())) { + verifyAllocation(lir, preAllocMap, context); + } + } + + protected Map saveInstructionsPreAlloc(LIR lir) { + Map preallocMap = new EconomicHashMap<>(Equivalence.IDENTITY); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + ArrayList instructions = lir.getLIRforBlock(block); + + RAVInstruction.Base previousInstr = null; + for (var instruction : instructions) { + if (this.isVirtualMove(instruction)) { + // Virtual moves (variable = MOV real register) are going to be removed by the allocator, + // but we still need the information about which variables are associated to which real + // registers, and so we store them. They are generally associated to other instructions + // that's why we append them here to the previous instruction (for example Label or Foreign Call) + // use these, if this instruction was deleted in the allocator, then they will be missing too. + assert previousInstr != null; + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var location = valueMov.getInput(); + var variable = LIRValueUtil.asVariable(valueMov.getResult()); + + var virtualMove = new RAVInstruction.ValueMove(instruction, variable, location); + previousInstr.addVirtualMove(virtualMove); + continue; // No need to store virtual move here, it is stored into previous instruction. + } + + boolean speculative = false; + if (this.isSpeculativeMove(instruction)) { + speculative = true; + // Speculative moves are in form ry = MOVE vx, which could be removed if variable + // ends up being allocated to the same register as ry. If it was removed + // we need to re-add it because it holds important information about where value of + // this variable is placed - for label resolution after the label. + assert previousInstr != null; + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var variable = LIRValueUtil.asVariable(valueMov.getInput()); + var register = valueMov.getResult(); + + var virtualMove = new RAVInstruction.ValueMove(instruction, variable, register); + previousInstr.addSpeculativeMove(virtualMove); + } + + var opRAVInstr = new RAVInstruction.Op(instruction); + + instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); + instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); + instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); + instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); + instruction.forEachState(opRAVInstr.stateValues.copyOriginalProc); + + preallocMap.put(instruction, opRAVInstr); + + if (!speculative) { + previousInstr = opRAVInstr; + } + } + } + return preallocMap; + } + + protected void verifyAllocation(LIR lir, Map preallocMap, AllocationContext context) { + var instructions = getVerifierInstructions(lir, preallocMap, context); + var verifier = new RegisterAllocationVerifier(lir, instructions, getRegisterAllocationConfig(context)); + + try { + verifier.run(); + } catch (RAVException | RAVError e) { + var debugCtx = lir.getDebug(); + + if (debugCtx.isDumpEnabled(DebugContext.VERBOSE_LEVEL)) { + var debugPath = debugCtx.getDumpPath(".rav.txt", false); + + try { + PrintStream output = new PrintStream(debugPath); + output.println("Register Allocation Verification failure:"); + output.println(e.getMessage()); + output.println(); + VerifierPrinter.print(output, lir, instructions); + } catch (FileNotFoundException ignored) { + } + + // Keep original message with class path prefix and add debug path info + // to the end so it's easier to access. + throw new RAVException(e + ", see debug file " + debugPath, e); + } + + throw e; + } + } + + /** + * Retrieve RegisterAllocationConfig from context, this function + * is here so it can be overwritten by a test when it needs to + * change the config. + * + * @param context Current phase context + * @return Instance of RegisterAllocationConfig + */ + protected RegisterAllocationConfig getRegisterAllocationConfig(AllocationContext context) { + return context.registerAllocationConfig; + } + + /** + * Process instructions after allocation and create the Verifier IR. + * Using previously stored instructions from the PreAlloc phase. + * + * @param lir LIR + * @param preallocMap Pre-allocation map to keep track of virtual values + * @return Verifier IR + */ + protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext context) { + Map definedVariables = new EconomicHashMap<>(); + var presentInstructions = preprocessAllocatedInstructions(lir, preallocMap, definedVariables); + + BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + var instructionList = new ArrayList(); + + ArrayList instructions = lir.getLIRforBlock(block); + for (var instruction : instructions) { + var rAVInstr = preallocMap.get(instruction); + if (rAVInstr == null) { + var movOp = this.getRAVMoveInstruction(instruction); + if (movOp != null) { + instructionList.add(movOp); + continue; + } + + throw new UnknownInstructionError(instruction, block); + } + + var opRAVInstr = (RAVInstruction.Op) rAVInstr; + + instruction.forEachInput(opRAVInstr.uses.copyCurrentProc); + instruction.forEachOutput(opRAVInstr.dests.copyCurrentProc); + instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); + instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); + instruction.forEachState(opRAVInstr.stateValues.copyCurrentProc); + + instructionList.add(opRAVInstr); + var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); + if (!speculativeMoves.isEmpty()) { + var readdedMoves = handleSpeculativeMoves(opRAVInstr, presentInstructions, definedVariables); + instructionList.addAll(readdedMoves); + } + + var virtualMoves = opRAVInstr.getVirtualMoveList(); + instructionList.addAll(virtualMoves); + } + + blockInstructions.put(block, instructionList); + } + + return blockInstructions; + } + + /** + * Iterate over every instruction after allocation save it to a set + * to see if speculative moves should be re-added or not and also + * track if variable has been defined before. + * + * @param lir LIR + * @param preallocMap Map of instructions before allocation + * @param definedVariables Output map, set defined variables here + * @return Set of instructions present after allocation + */ + protected Set preprocessAllocatedInstructions(LIR lir, Map preallocMap, Map definedVariables) { + Set presentInstructions = new EconomicHashSet<>(); + for (var blockId : lir.getBlocks()) { + BasicBlock block = lir.getBlockById(blockId); + ArrayList instructions = lir.getLIRforBlock(block); + + for (var instruction : instructions) { + presentInstructions.add(instruction); + + var rAVInstr = preallocMap.get(instruction); + if (rAVInstr instanceof RAVInstruction.Op op) { + for (int i = 0; i < op.dests.count; i++) { + if (op.dests.orig[i].isVariable()) { + var variable = op.dests.orig[i].asVariable(); + definedVariables.put(variable, op); + } + } + } + } + } + return presentInstructions; + } + + /** + * Handle speculative moves that should be re-added back to the IR + * to keep verification information in-tact, based on instructions + * present after allocation and variables defined by them. + * + * @param op Op that holds these speculatives + * @param presentInstructions Instructions present in the IR in form of a Map + * @param definedVariables Variables already defined + * @return List of speculative moves that need to be added bakc + */ + protected List handleSpeculativeMoves(RAVInstruction.Op op, Set presentInstructions, Map definedVariables) { + List toAdd = new ArrayList<>(); + for (var speculativeMove : op.getSpeculativeMoveList()) { + if (presentInstructions.contains(speculativeMove.getLIRInstruction())) { + continue; + } + + if (!speculativeMove.location.isVariable() && speculativeMove.variableOrConstant.isVariable()) { + var variable = speculativeMove.variableOrConstant.asVariable(); + var variableDefInstr = definedVariables.get(variable); + if (variableDefInstr == null) { + continue; + } + + if (variableDefInstr.lirInstruction instanceof StandardOp.LabelOp && variableDefInstr.lirInstruction == op.lirInstruction) { + for (int i = 0; i < op.dests.count; i++) { + var orig = op.dests.orig[i]; + if (!orig.isVariable() || op.dests.curr[i] != null) { + continue; + } + + // Add speculative instruction location back to the label + // where it's missing, only when speculative move is part + // of said label. + op.dests.curr[i] = speculativeMove.location; + } + } + + continue; + } + + toAdd.add(speculativeMove); + } + return toAdd; + } + + /** + * Create Register Verifier Instruction that was created by the Register Allocator. + * Generally speaking, it's always a move instruction, other ones return null. + * + * @param instruction LIR Instruction newly created by Register Allocator + * @return Spill, Reload, Move or null if instruction is not a move + */ + protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + if (instruction.isLoadConstantOp()) { + var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); + var constant = constatLoad.getConstant(); + var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot + + // Constant materialization result + return new RAVInstruction.ValueMove(instruction, new ConstantValue(result.getValueKind(), constant), result); + } + + return null; + } + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + + var input = valueMov.getInput(); + var result = valueMov.getResult(); + + if (LIRValueUtil.isStackSlotValue(input) && ValueUtil.isRegister(result)) { + return new RAVInstruction.Reload(instruction, ValueUtil.asRegisterValue(result), input); + } else if (LIRValueUtil.isStackSlotValue(result) && ValueUtil.isRegister(input)) { + return new RAVInstruction.Spill(instruction, result, ValueUtil.asRegisterValue(input)); + } else if (ValueUtil.isRegister(input) && ValueUtil.isRegister(result)) { + return new RAVInstruction.RegMove(instruction, ValueUtil.asRegisterValue(input), ValueUtil.asRegisterValue(result)); + } else if (LIRValueUtil.isStackSlotValue(input) && LIRValueUtil.isStackSlotValue(result)) { + return new RAVInstruction.StackMove(instruction, input, result); + } + + return null; + } + + /** + * Determines if instruction is a virtual move, a virtual move is + * a move instruction that moves a real register value into a variable, + * which is something that will always get removed from the final allocated + * IR. + *

+ * This information is important to the verification process and needs to + * be part of the Verifier IR. + *

+ * + * @param instruction LIR instruction we are looking at + * @return true, if instruction is a virtual move, otherwise false + */ + protected boolean isVirtualMove(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return false; + } + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var input = valueMov.getInput(); + return (ValueUtil.isRegister(input) || LIRValueUtil.isStackSlotValue(input)) && LIRValueUtil.isVariable(valueMov.getResult()); + } + + /** + * Determines if a move is speculative - it could potentially be + * removed, but hold important information to the verification process. + *

+ * For example, this happens for a move between two variables and after + * allocation locations are equal, making the move redundant. + *

+ * + * @param instruction LIR instruction we are looking at + * @return true, if instruction is a speculative move, otherwise false + */ + protected boolean isSpeculativeMove(LIRInstruction instruction) { + if (!instruction.isValueMoveOp()) { + return false; + } + + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); + var result = valueMov.getResult(); // Result could be variable or register + return (ValueUtil.isRegister(result) || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java index e507f67356bf..3f72cc02b920 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java @@ -89,7 +89,6 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); - this.blockStates = new BlockMap<>(cfg); this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); @@ -98,7 +97,7 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b /** * For every block, we need to calculate its entry state * which is a combination of states of blocks that are its - * predecessors, merged into a state we use to verify + * predecessors, we use to verify * that inputs to instructions are correct symbols based * on instructions before allocation. */ @@ -106,7 +105,7 @@ public void calculateEntryBlocks() { Queue> worklist = new ArrayDeque<>(); var startBlock = this.lir.getControlFlowGraph().getStartBlock(); - this.blockEntryStates.put(startBlock, new MergedBlockVerifierState(startBlock, registerAllocationConfig, constantMaterializationConflictResolver)); + this.blockEntryStates.put(startBlock, createNewBlockState(startBlock)); worklist.add(this.lir.getControlFlowGraph().getStartBlock()); while (!worklist.isEmpty()) { @@ -123,26 +122,27 @@ public void calculateEntryBlocks() { for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); + MergedBlockVerifierState succState; if (this.blockEntryStates.get(succ) == null) { - var succState = new MergedBlockVerifierState(succ, state); - - this.blockEntryStates.put(succ, succState); - worklist.remove(succ); - worklist.add(succ); - continue; + succState = new MergedBlockVerifierState(succ, state); + } else { + succState = this.blockEntryStates.get(succ); + if (!succState.meetWith(state)) { + continue; + } } - var succState = this.blockEntryStates.get(succ); - if (succState.meetWith(state)) { - // State changed or labels have not yet been determined, add to worklist - this.blockEntryStates.put(succ, succState); - worklist.remove(succ); // Always at the end, for predecessors to be processed first. - worklist.add(succ); - } + this.blockEntryStates.put(succ, succState); + worklist.remove(succ); // Always at the end, for predecessors to be processed first. + worklist.add(succ); } } } + protected MergedBlockVerifierState createNewBlockState(BasicBlock block) { + return new MergedBlockVerifierState(block, registerAllocationConfig, constantMaterializationConflictResolver); + } + /** * Verify every instruction input. */ @@ -152,13 +152,8 @@ public void verifyInstructionInputs() { var state = this.blockEntryStates.get(block); var instructions = this.blockInstructions.get(block); - RAVInstruction.Op labelInstrOfSucc = null; - if (block.getSuccessorCount() == 1) { - labelInstrOfSucc = (RAVInstruction.Op) this.blockInstructions.get(block.getSuccessorAt(0)).getFirst(); - } - for (var instr : instructions) { - state.check(instr, block, labelInstrOfSucc); + state.check(instr, block); state.update(instr, block); } } @@ -175,11 +170,10 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { var block = this.lir.getBlockById(blockId); var state = this.blockEntryStates.get(block); var instructions = this.blockInstructions.get(block); - var labelInstr = (RAVInstruction.Op) instructions.getFirst(); for (var instr : instructions) { try { - state.check(instr, block, labelInstr); + state.check(instr, block); state.update(instr, block); } catch (RAVException e) { exceptions.add(e); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java deleted file mode 100644 index 687d88338dbf..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhase.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2026, 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.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.InstructionStateProcedure; -import jdk.graal.compiler.lir.LIRFrameState; -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.gen.LIRGenerationResult; -import jdk.graal.compiler.lir.phases.AllocationPhase; -import jdk.graal.compiler.options.EnumOptionKey; -import jdk.graal.compiler.options.Option; -import jdk.graal.compiler.options.OptionKey; -import jdk.graal.compiler.options.OptionType; -import jdk.graal.compiler.util.EconomicHashMap; -import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.code.ValueUtil; - -import java.io.FileNotFoundException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Verification phase for Register Allocation. - */ -public class RegisterAllocationVerifierPhase extends AllocationPhase { - public static class Options { - @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) - public static final OptionKey EnableRAVerifier = new OptionKey<>(false); - - @Option(help = "Substring necessary to be found for method to be verified", type = OptionType.Debug) - public static final OptionKey RAFilter = new OptionKey<>(null); - } - - /** - * Shared phase state. - */ - private final RegisterAllocationVerifierPhaseState state; - - public RegisterAllocationVerifierPhase(RegisterAllocationVerifierPhaseState state) { - this.state = state; - } - - public RegisterAllocationVerifierPhaseState getState() { - return state; - } - - @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - if (!state.shouldBeVerified(lirGenRes)) { - // Filter for compilation unit substring to run verification only on - // certain methods, cannot use MethodFilter here because I cannot - // access JavaMethod here. - return; - } - - var instructions = getVerifierInstructions(lirGenRes); - var verifier = new RegisterAllocationVerifier(lirGenRes.getLIR(), instructions, context.registerAllocationConfig); - - // For timers for time spent in pre-alloc and verification phases look for these metric keys: - // LIRPhaseTime_PreRegisterAllocationPhase & LIRPhaseTime_RegisterAllocationVerifierPhase - try { - verifier.run(); - } catch (RAVException | RAVError e) { - var lir = lirGenRes.getLIR(); - var debugCtx = lir.getDebug(); - - if (debugCtx.isDumpEnabled(DebugContext.VERBOSE_LEVEL)) { - var debugPath = debugCtx.getDumpPath(".rav.txt", false); - - try { - PrintStream output = new PrintStream(debugPath); - output.println("Register Allocation Verification failure:"); - output.println(e.getMessage()); - output.println(); - VerifierPrinter.print(output, lir, instructions); - } catch (FileNotFoundException ignored) { - } - - // Keep original message with class path prefix and add debug path info - // to the end so it's easier to access. - new RAVException(e + ", see debug file " + debugPath, e); - } - - throw e; - } - } - - /** - * Process instructions after allocation and create the Verifier IR. - * Using previously stored instructions from the PreAlloc phase. - * - * @param lirGenRes LIR generation result of this method - * @return Verifier IR - */ - protected BlockMap> getVerifierInstructions(LIRGenerationResult lirGenRes) { - var lir = lirGenRes.getLIR(); - var preallocMap = state.getInstructionMap(lirGenRes); - - Map definedVariables = new EconomicHashMap<>(); - Set presentInstructions = new EconomicHashSet<>(); - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - ArrayList instructions = lir.getLIRforBlock(block); - - for (var instruction : instructions) { - presentInstructions.add(instruction); - - var rAVInstr = preallocMap.get(instruction); - if (rAVInstr instanceof RAVInstruction.Op op) { - for (int i = 0; i < op.dests.count; i++) { - if (op.dests.orig[i].isVariable()) { - var variable = op.dests.orig[i].asVariable(); - definedVariables.put(variable, op); - } - } - } - } - } - - BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); - for (var blockId : lir.getBlocks()) { - BasicBlock block = lir.getBlockById(blockId); - var instructionList = new ArrayList(); - - ArrayList instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - var rAVInstr = preallocMap.get(instruction); - if (rAVInstr == null) { - var movOp = this.getRAVMoveInstruction(instruction); - if (movOp != null) { - instructionList.add(movOp); - continue; - } - - // TestCase: TruffleHotSpotCompilation-75393[TruffleSafepointTest.TestRootNode@f25a8e].cfg - throw new UnknownInstructionError(instruction, block); - } - - var opRAVInstr = (RAVInstruction.Op) rAVInstr; - - instruction.forEachInput(opRAVInstr.uses.copyCurrentProc); - instruction.forEachOutput(opRAVInstr.dests.copyCurrentProc); - instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); - instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); - instruction.forEachState(opRAVInstr.stateValues.copyCurrentProc); - instruction.forEachState(new InstructionStateProcedure() { - @Override - public void doState(LIRInstruction instruction, LIRFrameState state) { - if (state.topFrame == null) { - return; - } - - opRAVInstr.currFrameSlots = state.topFrame.values; - } - }); - - instructionList.add(opRAVInstr); - var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); - if (!speculativeMoves.isEmpty()) { - for (var speculativeMove : speculativeMoves) { - if (presentInstructions.contains(speculativeMove.getLIRInstruction())) { - continue; - } - - if (!speculativeMove.location.isVariable() && speculativeMove.variableOrConstant.isVariable()) { - var variable = speculativeMove.variableOrConstant.asVariable(); - var variableDefInstr = definedVariables.get(variable); - if (variableDefInstr == null) { - continue; - } - - if (variableDefInstr.lirInstruction instanceof StandardOp.LabelOp && variableDefInstr.lirInstruction == opRAVInstr.lirInstruction) { - for (int i = 0; i < opRAVInstr.dests.count; i++) { - var orig = opRAVInstr.dests.orig[i]; - if (!orig.isVariable() || opRAVInstr.dests.curr[i] != null) { - continue; - } - - opRAVInstr.dests.curr[i] = speculativeMove.location; - } - } - - continue; - } - - instructionList.add(speculativeMove); - } - } - - var virtualMoves = opRAVInstr.getVirtualMoveList(); - instructionList.addAll(virtualMoves); - - preallocMap.remove(instruction); - } - - blockInstructions.put(block, instructionList); - } - - state.deleteInstructionMap(lirGenRes); - return blockInstructions; - } - - /** - * Create Register Verifier Instruction that was created by the Register Allocator. - * Generally speaking, it's always a move instruction, other ones return null. - * - * @param instruction LIR Instruction newly created by Register Allocator - * @return Spill, Reload, Move or null if instruction is not a move - */ - protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - if (instruction.isLoadConstantOp()) { - var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); - var constant = constatLoad.getConstant(); - var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot - - // Constant materialization result - return new RAVInstruction.ValueMove(instruction, new ConstantValue(result.getValueKind(), constant), result); - } - - return null; - } - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - - var input = valueMov.getInput(); - var result = valueMov.getResult(); - - if (LIRValueUtil.isStackSlotValue(input) && ValueUtil.isRegister(result)) { - return new RAVInstruction.Reload(instruction, ValueUtil.asRegisterValue(result), input); - } else if (LIRValueUtil.isStackSlotValue(result) && ValueUtil.isRegister(input)) { - return new RAVInstruction.Spill(instruction, result, ValueUtil.asRegisterValue(input)); - } else if (ValueUtil.isRegister(input) && ValueUtil.isRegister(result)) { - return new RAVInstruction.RegMove(instruction, ValueUtil.asRegisterValue(input), ValueUtil.asRegisterValue(result)); - } else if (LIRValueUtil.isStackSlotValue(input) && LIRValueUtil.isStackSlotValue(result)) { - return new RAVInstruction.StackMove(instruction, input, result); - } - - return null; - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java deleted file mode 100644 index 56c7e27be812..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifierPhaseState.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2026, 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.alloc.verifier; - -import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.lir.LIRInstruction; -import jdk.graal.compiler.lir.gen.LIRGenerationResult; -import jdk.graal.compiler.options.OptionValues; -import jdk.graal.compiler.util.EconomicHashMap; -import org.graalvm.collections.Equivalence; - -import java.util.Map; - -/** - * Phase state shared by the preallocation and verification phases, - * pertaining mostly to shared config and Verifier IR. - */ -public class RegisterAllocationVerifierPhaseState { - public String filterStr; - - /** - * Mapping between LIRGenerationResult and Map of LIR instructions to Verifier instructions. - */ - protected Map> verifierInstructions; - - public RegisterAllocationVerifierPhaseState(OptionValues options) { - this.verifierInstructions = new EconomicHashMap<>(Equivalence.IDENTITY); - - this.filterStr = RegisterAllocationVerifierPhase.Options.RAFilter.getValue(options); - } - - /** - * Should this method be verified? Filter when filterStr is set, - * use ful debugging purposes. - * - * @param lirGenRes LIR generation result describing the method - * @return true, if method should be verified, otherwise false - */ - public boolean shouldBeVerified(LIRGenerationResult lirGenRes) { - var compUnitName = lirGenRes.getCompilationUnitName(); - // Filter for compilation unit substring to run verification only on - // certain methods, cannot use MethodFilter here because I cannot - // access JavaMethod here. - return filterStr == null || compUnitName.contains(filterStr); - } - - /** - * Create a new instruction map for this method. - * - * @param lirGenRes LIR generation result of this method - * @return New instruction map - */ - public Map createInstructionMap(LIRGenerationResult lirGenRes) { - Map idMap = new EconomicHashMap<>(Equivalence.IDENTITY); - this.verifierInstructions.put(lirGenRes, idMap); - return idMap; - } - - /** - * Retrieve an existing instruction map for this method. - * - * @param lirGenRes LIR generation result of this method - * @return Old instruction map - */ - public Map getInstructionMap(LIRGenerationResult lirGenRes) { - if (!this.verifierInstructions.containsKey(lirGenRes)) { - GraalError.shouldNotReachHere("PreAlloc phase did not run for " + lirGenRes.getCompilationUnitName()); - } - - return this.verifierInstructions.get(lirGenRes); - } - - /** - * Delete existing instruction map for this method after - * is it is no longer needed. - * - * @param lirGenRes LIR generation result of this method - */ - public void deleteInstructionMap(LIRGenerationResult lirGenRes) { - verifierInstructions.remove(lirGenRes); - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java index 5aaa4e8d7fdf..352742180582 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java @@ -26,9 +26,8 @@ import jdk.graal.compiler.debug.Assertions; import jdk.graal.compiler.lir.alloc.AllocationStageVerifier; -import jdk.graal.compiler.lir.alloc.verifier.PreRegisterAllocationPhase; -import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhase; -import jdk.graal.compiler.lir.alloc.verifier.RegisterAllocationVerifierPhaseState; +import jdk.graal.compiler.lir.alloc.RegisterAllocationPhase; +import jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase; import jdk.graal.compiler.lir.stackslotalloc.LSStackSlotAllocator; import jdk.graal.compiler.lir.stackslotalloc.SimpleStackSlotAllocator; import jdk.graal.compiler.lir.alloc.lsra.LinearScanPhase; @@ -42,22 +41,25 @@ public AllocationStage(OptionValues options) { appendPhase(new MarkBasePointersPhase()); appendPhase(new LinearScanPhase()); - // For now, verify before stack allocator - if (RegisterAllocationVerifierPhase.Options.EnableRAVerifier.getValue(options)) { - var sharedState = new RegisterAllocationVerifierPhaseState(options); - - var preAllocRAVPhase = new PreRegisterAllocationPhase(sharedState); - var ravPhase = new RegisterAllocationVerifierPhase(sharedState); - - prependPhase(preAllocRAVPhase); - appendPhase(ravPhase); - } - // build frame map + LIRPhase stackAllocator = null; if (LSStackSlotAllocator.Options.LIROptLSStackSlotAllocator.getValue(options)) { - appendPhase(new LSStackSlotAllocator()); + stackAllocator = new LSStackSlotAllocator(); + } else { + stackAllocator = new SimpleStackSlotAllocator(); + } + + if (RegAllocVerifierPhase.Options.EnableRAVerifier.getValue(options)) { + // Wrap used register allocator with the verifier to check it's output + // based on the input with variables + var iterator = this.findPhase(RegisterAllocationPhase.class); + if (iterator != null) { + var allocator = (RegisterAllocationPhase) iterator.previous(); + // If not found, then let later reg alloc check throw + iterator.set(new RegAllocVerifierPhase(allocator, stackAllocator)); + } } else { - appendPhase(new SimpleStackSlotAllocator()); + appendPhase(stackAllocator); } if (Assertions.detailedAssertionsEnabled(options)) { From 4b211b5751befe07f193c094315a1571c5212543 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 4 Mar 2026 22:46:14 +0100 Subject: [PATCH 061/112] Make stack allocator verification optional --- ...nStateMap.java => AllocationStateMap.java} | 19 +++-- ...fierState.java => BlockVerifierState.java} | 70 ++++++++++++------- ...nstantMaterializationConflictResolver.java | 2 +- .../verifier/FromUsageResolverGlobal.java | 27 ++++--- .../lir/alloc/verifier/RAVInstruction.java | 32 ++++++--- ...ionVerifier.java => RegAllocVerifier.java} | 50 +++++++------ .../alloc/verifier/RegAllocVerifierPhase.java | 63 +++++++++++++++-- .../compiler/lir/phases/AllocationStage.java | 2 +- 8 files changed, 184 insertions(+), 81 deletions(-) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{MergedAllocationStateMap.java => AllocationStateMap.java} (88%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{MergedBlockVerifierState.java => BlockVerifierState.java} (89%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{RegisterAllocationVerifier.java => RegAllocVerifier.java} (78%) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java similarity index 88% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java index 949d1a0da150..7a568caf0cb1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedAllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java @@ -33,9 +33,18 @@ import java.util.Set; /** - * Mapping between a location and it's AllocationState. + * Mapping between a location and allocation state, + * that stores one of these: + * - Unknown - our null state, nothing was stored yet + * - Value - symbol that is stored at said location + * - Conflict - set of Values that are supposed to be at same location + * + * Conflicts are resolved by assigning new Value to same location. + * Otherwise, they cannot be used. Value can store register, stack slot, + * constant, but most importantly variables used before allocation. These + * are what we are checking with the verification process. */ -public class MergedAllocationStateMap { +public class AllocationStateMap { protected BasicBlock block; /** @@ -49,13 +58,13 @@ public class MergedAllocationStateMap { */ protected RegisterAllocationConfig registerAllocationConfig; - public MergedAllocationStateMap(BasicBlock block, RegisterAllocationConfig registerAllocationConfig) { + public AllocationStateMap(BasicBlock block, RegisterAllocationConfig registerAllocationConfig) { internalMap = new EconomicHashMap<>(); this.block = block; this.registerAllocationConfig = registerAllocationConfig; } - public MergedAllocationStateMap(BasicBlock block, MergedAllocationStateMap other) { + public AllocationStateMap(BasicBlock block, AllocationStateMap other) { internalMap = new EconomicHashMap<>(other.internalMap); this.block = block; registerAllocationConfig = other.registerAllocationConfig; @@ -145,7 +154,7 @@ public Set getValueLocations(RAValue value) { * @param source Predecessor merging to here * @return Was this map changed? */ - public boolean mergeWith(MergedAllocationStateMap source) { + public boolean mergeWith(AllocationStateMap source) { boolean changed = false; for (var entry : source.internalMap.entrySet()) { if (!this.internalMap.containsKey(entry.getKey())) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java similarity index 89% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 1cd654c2d2c9..c1ecd1f64971 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MergedBlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -31,25 +31,20 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.vm.ci.code.StackLockValue; import jdk.vm.ci.code.ValueUtil; -import jdk.vm.ci.meta.AllocatableValue; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaValue; import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; -import java.util.ArrayList; import java.util.List; /** * Verification state a block is in. */ -public class MergedBlockVerifierState { +public class BlockVerifierState { /** * Map maintaining mapping between locations and their state. */ - public MergedAllocationStateMap values; + public AllocationStateMap values; /** * Register allocation config we use to check if only @@ -62,32 +57,42 @@ public class MergedBlockVerifierState { */ protected ConflictResolver conflictConstantResolver; - public MergedBlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver) { - this.values = new MergedAllocationStateMap(block, registerAllocationConfig); + public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver) { + this.values = new AllocationStateMap(block, registerAllocationConfig); this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; } - protected MergedBlockVerifierState(BasicBlock block, MergedBlockVerifierState other) { + protected BlockVerifierState(BasicBlock block, BlockVerifierState other) { this.registerAllocationConfig = other.registerAllocationConfig; this.conflictConstantResolver = other.conflictConstantResolver; - this.values = new MergedAllocationStateMap(block, other.values); + this.values = new AllocationStateMap(block, other.values); } - public MergedAllocationStateMap getValues() { + public AllocationStateMap getValues() { return values; } /** - * Merge states of block and it's predecessor. + * Merge states of block and it's predecessor. This process + * creates a new state based on contents of the predecessor, + * creating conflicts where current locations do not match. * * @param other Predecessor of this block * @return Was this state changed? */ - public boolean meetWith(MergedBlockVerifierState other) { + public boolean meetWith(BlockVerifierState other) { return this.values.mergeWith(other.getValues()); } + /** + * Check state values stored in LIRFrameState same way other operands, + * except we skip those where the state values are incorrectly stored, + * after stack allocation, because some values are no longer present. + * + * @param op Operation for which we are checking state values + * @param block Block where this operation is located + */ protected void checkStateValues(RAVInstruction.Op op, BasicBlock block) { if (!op.hasCompleteState()) { // Some values are null after allocation because of stack slot allocator @@ -113,6 +118,19 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. } } + /** + * Check that original variable matches symbol stored at + * the current location in the allocation state map. + *

+ * We also check that kinds match and possibly + * rematerialize variables at this point in the + * state map. + * + * @param orig Original variable + * @param curr Current location + * @param op Operation where these are used + * @param block Block where this operation is + */ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op, BasicBlock block) { assert orig != null; @@ -124,12 +142,6 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op, Ba return; } - // Verifying Stack Allocator: - // Here we also need to handle stateValues being null - // because sometimes a StackLockValue is used and after - // stack allocation it is no longer given to us when - // iterating. This also causes issues in the if statement - // below items are not shifted in curr array throw new MissingLocationError(op.lirInstruction, block, orig); } @@ -231,7 +243,6 @@ protected boolean kindsEqual(RAValue orig, RAValue curr) { *

* We need to ignore the cast value because the currently stored * value will not be cast. - *

* * @param orig Original variable * @param fromState Value stored in state of the current location @@ -368,6 +379,8 @@ protected void checkOperandFlags(RAVInstruction.ValueArrayPair values, RAVInstru /** * Update the current state based on outputs of this instruction. + * Setting contents of current location in allocation state map to + * the symbol that was present before allocation was completed. * * @param instruction Instruction we update state from * @param block Block where it is located @@ -382,7 +395,10 @@ public void update(RAVInstruction.Base instruction, BasicBlock block) { } /** - * Update the state using a generic operation. + * Update the state using a generic operation, + * based on contents of its output array, storing + * symbols pre-allocation to current locations after + * allocation. * * @param op Operation we update state from * @param block Block where it is located @@ -432,9 +448,9 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { * Update block state with a safe point list of live references deemed by the GC, * any other references not included in said list are to be set as unknown so * there's no freed pointer use. - *

+ * * Not in use currently, because we do not have a reliable list of live references. - *

+ * * Reference test case: AOTTutorial * * @param references List of references deemed as live by the GC @@ -481,7 +497,7 @@ protected void updateWithSafePoint(List references) { * @param block Block where it is located */ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove, BasicBlock block) { - if (valueMove.location.isVariable()) { + if (valueMove.getLocation().isVariable()) { // Whenever there is a move between two variables, // we need to change every location containing the old variable (rhs - source) // to the new variable (lhs - destination) @@ -490,10 +506,10 @@ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove, BasicBloc // TestCase: BoxingTest.boxBoolean var locations = this.values.getValueLocations(valueMove.variableOrConstant); for (var location : locations) { - this.values.put(location, new ValueAllocationState(valueMove.location, valueMove, block)); + this.values.put(location, new ValueAllocationState(valueMove.getLocation(), valueMove, block)); } } else { - this.values.put(valueMove.location, new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); + this.values.put(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index dbcf8c399a1d..158188a877ed 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -197,7 +197,7 @@ protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllo // Cannot be rematerialized to stack var source = state.getSource(); if (source instanceof RAVInstruction.ValueMove move) { - var location = move.location.getValue(); + var location = move.getLocation().getValue(); return LIRValueUtil.isStackSlotValue(location); } else { throw new RematerializedConstantSourceMissingError(source, variable); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 4f9204c1663a..7a355cae6379 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -52,7 +52,8 @@ public class FromUsageResolverGlobal { public Map reached; public Map> firstUsages; public Map initialLocations; - public Map aliasMap; + public Map> aliasMap; + public Map> aliasBlockMap; public BlockMap blockUsageMap; // Entry blocks! public List> endBlocks; @@ -106,6 +107,7 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b this.firstUsages = new EconomicHashMap<>(); this.initialLocations = new EconomicHashMap<>(); this.aliasMap = new EconomicHashMap<>(); + this.aliasMap = new EconomicHashMap<>(); this.aliasBlockMap = new EconomicHashMap<>(); this.endBlocks = new ArrayList<>(); @@ -233,7 +235,10 @@ protected void initializeUsages() { } aliasBlockMap.put(variable, block); - aliasMap.put(variable, succLabel.dests.orig[i].asVariable()); + + var aliasedVariables = aliasMap.getOrDefault(alias, new ArrayList<>()); + aliasedVariables.add(variable); + aliasMap.put(alias, aliasedVariables); } } @@ -270,7 +275,10 @@ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction firstUsages.get(op).add(variable); initialLocations.put(variable, values.curr[i]); - aliasMap.remove(variable); + for (var entry : aliasMap.entrySet()) { + var aliasedVariables = entry.getValue(); + aliasedVariables.remove(variable); + } } } } @@ -323,19 +331,18 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc // Variables that are passed into jumps without any other usage are aliases // for same variable in successor label, whenever said variable is resolved // we now have a location for this variable and can take other moves into account. - for (var entry : aliasMap.entrySet()) { - var aliased = entry.getValue(); - if (variable.equals(aliased)) { - var alias = entry.getKey(); - var aliasBlock = aliasBlockMap.get(alias); + if (aliasMap.containsKey(variable)) { + var aliasedVariables = aliasMap.get(variable); + for (var aliases : aliasedVariables) { + var aliasBlock = aliasBlockMap.get(aliases); if (blockUsageMap.get(aliasBlock) == null) { this.blockUsageMap.put(aliasBlock, new BlockUsage()); } var aliasBlockUsage = blockUsageMap.get(aliasBlock); - aliasBlockUsage.locations.put(alias, location); - aliasBlockUsage.reached.add(alias); + aliasBlockUsage.locations.put(aliases, location); + aliasBlockUsage.reached.add(aliases); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 3c7252ef1101..3a2faf08608f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -27,15 +27,14 @@ import jdk.graal.compiler.hotspot.amd64.AMD64HotSpotSafepointOp; import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.aarch64.AArch64Call; import jdk.graal.compiler.lir.amd64.AMD64Call; -import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaValue; +import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; import java.util.ArrayList; @@ -447,10 +446,7 @@ public static class ValueMove extends Base { */ public RAValue variableOrConstant; - /** - * Where variable (or constant) is being stored. - */ - public RAValue location; // Can also be another variable! + protected RAValue location; // Can also be another variable! public ValueMove(LIRInstruction instr, Value variableOrConstant, Value location) { super(instr); @@ -459,7 +455,27 @@ public ValueMove(LIRInstruction instr, Value variableOrConstant, Value location) } public String toString() { - return location.toString() + " = VIRTMOVE " + variableOrConstant.toString(); + return getLocation().toString() + " = VIRTMOVE " + variableOrConstant.toString(); + } + + public void setLocation(RAValue location) { + this.location = location; + } + + /** + * Where variable (or constant) is being stored. + */ + public RAValue getLocation() { + if (LIRValueUtil.isVirtualStackSlot(location.getValue())) { + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(lirInstruction); + var input = valueMov.getInput(); + if (ValueUtil.isStackSlot(input)) { + // Change vstack to allocated stack slot, because it was changed + location.value = input; + } + } + + return location; } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java similarity index 78% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 3f72cc02b920..6b0d6c92a295 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegisterAllocationVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -40,22 +40,17 @@ * locations and checking validity of every location to variable * correspondence. */ -public class RegisterAllocationVerifier { +public class RegAllocVerifier { /** * Verifier IR that abstracts LIR instructions * and marks moves inserted by the allocator. */ protected BlockMap> blockInstructions; - /** - * Current state of said block during processing. - */ - protected BlockMap blockStates; - /** * State of the block on entry, calculated from its predecessors. */ - protected BlockMap blockEntryStates; + protected BlockMap blockEntryStates; /** * LIR necessary for to access the program graph. @@ -80,7 +75,7 @@ public class RegisterAllocationVerifier { */ protected ConflictResolver constantMaterializationConflictResolver; - public RegisterAllocationVerifier(LIR lir, BlockMap> blockInstructions, RegisterAllocationConfig registerAllocationConfig) { + public RegAllocVerifier(LIR lir, BlockMap> blockInstructions, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; this.registerAllocationConfig = registerAllocationConfig; @@ -89,7 +84,6 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); - this.blockStates = new BlockMap<>(cfg); this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); } @@ -97,9 +91,10 @@ public RegisterAllocationVerifier(LIR lir, BlockMap> b /** * For every block, we need to calculate its entry state * which is a combination of states of blocks that are its - * predecessors, we use to verify - * that inputs to instructions are correct symbols based - * on instructions before allocation. + * predecessors, we get after reached a fixed point state, + * where no entry state is changed. + * + * This is necessary to verify instruction inputs correctly. */ public void calculateEntryBlocks() { Queue> worklist = new ArrayDeque<>(); @@ -113,18 +108,17 @@ public void calculateEntryBlocks() { var instructions = this.blockInstructions.get(block); // Create new entry state for successor blocks out of current block state - var state = new MergedBlockVerifierState(block, this.blockEntryStates.get(block)); + var state = new BlockVerifierState(block, this.blockEntryStates.get(block)); for (var instr : instructions) { state.update(instr, block); } - this.blockStates.put(block, state); for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); - MergedBlockVerifierState succState; + BlockVerifierState succState; if (this.blockEntryStates.get(succ) == null) { - succState = new MergedBlockVerifierState(succ, state); + succState = new BlockVerifierState(succ, state); } else { succState = this.blockEntryStates.get(succ); if (!succState.meetWith(state)) { @@ -139,12 +133,16 @@ public void calculateEntryBlocks() { } } - protected MergedBlockVerifierState createNewBlockState(BasicBlock block) { - return new MergedBlockVerifierState(block, registerAllocationConfig, constantMaterializationConflictResolver); + protected BlockVerifierState createNewBlockState(BasicBlock block) { + return new BlockVerifierState(block, registerAllocationConfig, constantMaterializationConflictResolver); } /** - * Verify every instruction input. + * By using the entry states calculated in step beforehand, + * we check input of every instruction to see that it matches + * symbols before allocation, after wards we update the state + * so the next instruction has correct state at said instruction + * input. */ public void verifyInstructionInputs() { for (var blockId : this.lir.getBlocks()) { @@ -187,9 +185,17 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { } /** - * Run the verification process, including label variable - * resolution, handling of materialized constants, calculating - * entry states for every block. + * Run verification based on verifier IR created in phase beforehand, + * resolving stripped label variable locations back, calculating entry state + * for every block so that at the end we can verify inputs of instructions + * match variables present before allocation. + * + * The issues we are looking to catch are mostly about making sure that + * order of spills, reloads and moves is correct and that used location + * after stores the symbol that is supposed to be there. + * + * We also make sure that kinds are still matching, operand flags aren't violated, + * alive location not being used as temp or output of same instruction. */ public void run() { this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 166a6e3d0768..7eef71f520ff 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -57,18 +57,29 @@ /** * Verification phase for Register Allocation, wraps around * the actual allocator and validates that order of spills, reloads - * and moves is correct and that variables before allocations - * are actually stored in concrete locations chosen by the allocator. - *

+ * and moves is correct and that variables before allocation + * are actually stored in current locations chosen by the allocator. + * * Needs to extend RegisterAllocationPhase to not throw an exception. */ public class RegAllocVerifierPhase extends RegisterAllocationPhase { public static class Options { @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) public static final OptionKey EnableRAVerifier = new OptionKey<>(false); + + @Option(help = "Verify output of stack allocator with register allocator", type = OptionType.Debug) + public static final OptionKey VerifyStackAllocator = new OptionKey<>(true); } + /** + * Register allocator we are verifying output of. + */ protected RegisterAllocationPhase allocator; + + /** + * Stack allocator, if not null, being verified + * simultaneously with reg allocator. + */ protected LIRPhase stackSlotAllocator; private static final TimerKey PreallocTimer = DebugContext.timer("RAV_PreAlloc"); @@ -97,6 +108,25 @@ public void setAllocator(RegisterAllocationPhase allocator) { this.allocator = allocator; } + /** + * Get stack allocator being verified with register allocator if not null. + * + * @return Stack allocator or null + */ + public LIRPhase getStackSlotAllocator() { + return stackSlotAllocator; + } + + /** + * Set stack allocator, used by tests + * + * @param stackSlotAllocator Stack allocator + */ + public void setStackSlotAllocator(LIRPhase stackSlotAllocator) { + this.stackSlotAllocator = stackSlotAllocator; + } + + @SuppressWarnings("try") @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { assert allocator != null : "No register allocator present for verification"; @@ -108,16 +138,28 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo preAllocMap = saveInstructionsPreAlloc(lir); } + boolean verifyStackAlloc = Options.VerifyStackAllocator.getValue(lirGenRes.getLIR().getOptions()); + allocator.apply(target, lirGenRes, context); - if (stackSlotAllocator != null) { + if (stackSlotAllocator != null && verifyStackAlloc) { stackSlotAllocator.apply(target, lirGenRes, context); } try (final DebugCloseable t = VerifierTimer.start(lir.getDebug())) { verifyAllocation(lir, preAllocMap, context); } + + if (stackSlotAllocator != null && !verifyStackAlloc) { + stackSlotAllocator.apply(target, lirGenRes, context); + } } + /** + * Save instruction before allocation to keep track of symbols used in instructions. + * + * @param lir LIR + * @return Map of LIRInstruction to RAVInstruction that keeps symbols + */ protected Map saveInstructionsPreAlloc(LIR lir) { Map preallocMap = new EconomicHashMap<>(Equivalence.IDENTITY); for (var blockId : lir.getBlocks()) { @@ -178,9 +220,16 @@ protected Map saveInstructionsPreAlloc(LIR return preallocMap; } + /** + * Use information before allocation to verify output of allocator(s). + * + * @param lir LIR + * @param preallocMap Map of instructions before allocation + * @param context Allocation context + */ protected void verifyAllocation(LIR lir, Map preallocMap, AllocationContext context) { var instructions = getVerifierInstructions(lir, preallocMap, context); - var verifier = new RegisterAllocationVerifier(lir, instructions, getRegisterAllocationConfig(context)); + var verifier = new RegAllocVerifier(lir, instructions, getRegisterAllocationConfig(context)); try { verifier.run(); @@ -325,7 +374,7 @@ protected List handleSpeculativeMoves(RAVInstruction.O continue; } - if (!speculativeMove.location.isVariable() && speculativeMove.variableOrConstant.isVariable()) { + if (!speculativeMove.getLocation().isVariable() && speculativeMove.variableOrConstant.isVariable()) { var variable = speculativeMove.variableOrConstant.asVariable(); var variableDefInstr = definedVariables.get(variable); if (variableDefInstr == null) { @@ -342,7 +391,7 @@ protected List handleSpeculativeMoves(RAVInstruction.O // Add speculative instruction location back to the label // where it's missing, only when speculative move is part // of said label. - op.dests.curr[i] = speculativeMove.location; + op.dests.curr[i] = speculativeMove.getLocation(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java index 352742180582..9b8f04f623fe 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/phases/AllocationStage.java @@ -42,7 +42,7 @@ public AllocationStage(OptionValues options) { appendPhase(new LinearScanPhase()); // build frame map - LIRPhase stackAllocator = null; + LIRPhase stackAllocator; if (LSStackSlotAllocator.Options.LIROptLSStackSlotAllocator.getValue(options)) { stackAllocator = new LSStackSlotAllocator(); } else { From b07fce0415ce181c95702f58011deec07e1d39c5 Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 6 Mar 2026 10:44:27 +0100 Subject: [PATCH 062/112] Handle callee saved registers --- .../alloc/verifier/BlockVerifierState.java | 58 +++++++++++++++++-- .../verifier/ConflictedAllocationState.java | 5 +- .../lir/alloc/verifier/RegAllocVerifier.java | 18 +++++- 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index c1ecd1f64971..c654409dc673 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -57,16 +57,23 @@ public class BlockVerifierState { */ protected ConflictResolver conflictConstantResolver; + /** + * Block this state pertains to + */ + protected BasicBlock block; + public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver) { this.values = new AllocationStateMap(block, registerAllocationConfig); this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; + this.block = block; } protected BlockVerifierState(BasicBlock block, BlockVerifierState other) { this.registerAllocationConfig = other.registerAllocationConfig; this.conflictConstantResolver = other.conflictConstantResolver; this.values = new AllocationStateMap(block, other.values); + this.block = block; } public AllocationStateMap getValues() { @@ -429,9 +436,8 @@ protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { } } - // For calls, we hope that all saved registers are listed, even - // though they are not as per checking RegisterConfig.getCallerSavedRegisters() - // but using said list to clear them causes issues. + // For calls, temp lists all registers that are supposed to be caller saved, + // because sometimes there's a difference between RegisterConfig.getCallerSaveRegisters for (int i = 0; i < op.temp.count; i++) { var value = op.temp.curr[i]; if (value.isIllegal()) { @@ -459,7 +465,7 @@ protected void updateWithSafePoint(List references) { for (var entry : this.values.internalMap.entrySet()) { var state = entry.getValue(); if (state.isUnknown() || state.isConflicted()) { - continue; // Do not care, retain information + continue; // Retain information } var valueAllocState = (ValueAllocationState) state; @@ -487,6 +493,50 @@ protected void updateWithSafePoint(List references) { } } + /** + * Take list of callee saved registers and add them to the start + * block state with their own values as symbols in order to check + * that they were correctly retrieved at exit point. + */ + protected void updateCalleeSavedRegisters() { + var registers = this.registerAllocationConfig.getRegisterConfig().getCalleeSaveRegisters(); + if (registers == null) { + return; + } + + for (var reg : registers) { + var regValue = RARegister.create(reg.asValue()); + + // Save same registers as symbol, and later check if it was retrieved + this.values.putWithoutRegCheck(regValue, new ValueAllocationState(regValue, null, block)); + } + } + + /** + * At exit point, check that all callee saved registers were + * indeed correctly saved, by checking that the symbol stored + * in said registers is equal to the registers themselves. + */ + protected void checkCalleeSavedRegisters() { + var registers = this.registerAllocationConfig.getRegisterConfig().getCalleeSaveRegisters(); + if (registers == null) { + return; + } + + for (var reg : registers) { + var regValue = RARegister.create(reg.asValue()); + var state = this.values.get(regValue); + if (state instanceof ValueAllocationState valueAllocationState) { + if (valueAllocationState.getRAValue().equals(regValue)) { + // Same symbol as register means the value was retrieved safely + continue; + } + } + + throw new RAVException("Callee saved register " + regValue + " not recovered."); + } + } + /** * Update state with a ValueMove, if locations is concrete, * we set it to a variable/constant, if it's a variable to variable diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 0cc0165195df..1adbc8082de1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -43,6 +43,7 @@ public ConflictedAllocationState() { this.conflictedStates = new EconomicHashSet<>(); } + @SuppressWarnings("this-escape") public ConflictedAllocationState(ValueAllocationState state1, ValueAllocationState state2) { this(); addConflictedValue(state1); @@ -53,7 +54,7 @@ protected ConflictedAllocationState(Set conflictedStates) this.conflictedStates = new EconomicHashSet<>(conflictedStates); } - public final void addConflictedValue(ValueAllocationState state) { + public void addConflictedValue(ValueAllocationState state) { if (hasConflictedValue(state)) { return; } @@ -61,7 +62,7 @@ public final void addConflictedValue(ValueAllocationState state) { this.conflictedStates.add(state); } - public final boolean hasConflictedValue(ValueAllocationState valueAllocationState) { + public boolean hasConflictedValue(ValueAllocationState valueAllocationState) { for (var state : this.conflictedStates) { if (state.getRAValue().equals(valueAllocationState.getRAValue())) { return true; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 6b0d6c92a295..614d0230e9be 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -100,9 +100,11 @@ public void calculateEntryBlocks() { Queue> worklist = new ArrayDeque<>(); var startBlock = this.lir.getControlFlowGraph().getStartBlock(); - this.blockEntryStates.put(startBlock, createNewBlockState(startBlock)); - worklist.add(this.lir.getControlFlowGraph().getStartBlock()); + var startBlockState = createNewBlockState(startBlock); + startBlockState.updateCalleeSavedRegisters(); + this.blockEntryStates.put(startBlock, startBlockState); + worklist.add(startBlock); while (!worklist.isEmpty()) { var block = worklist.poll(); var instructions = this.blockInstructions.get(block); @@ -154,6 +156,10 @@ public void verifyInstructionInputs() { state.check(instr, block); state.update(instr, block); } + + if (block.getSuccessorCount() == 0) { + state.checkCalleeSavedRegisters(); + } } } @@ -177,6 +183,14 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { exceptions.add(e); } } + + try { + if (block.getSuccessorCount() == 0) { + state.checkCalleeSavedRegisters(); + } + } catch (RAVException e) { + exceptions.add(e); + } } if (!exceptions.isEmpty()) { From 919995df61f078abd7164ce366f9587cf481cd99 Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 6 Mar 2026 10:51:25 +0100 Subject: [PATCH 063/112] Change rav debug file format --- .../alloc/verifier/RegAllocVerifierPhase.java | 2 +- .../lir/alloc/verifier/VerifierPrinter.java | 99 +++++++++++++++---- 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 7eef71f520ff..bf4e0c16f0cc 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -244,7 +244,7 @@ protected void verifyAllocation(LIR lir, Map 0) { - blockHeaderSB.append(" <- "); - for (int i = 0; i < block.getPredecessorCount(); i++) { - blockHeaderSB.append(block.getPredecessorAt(i)).append(", "); + printBlockHeader(out, block); + for (var instruction : instructions.get(block)) { + out.println("\t" + instruction.toString() + " | " + instruction.getLIRInstruction().toString()); + if (instruction instanceof RAVInstruction.Op op && op.stateValues.count > 0) { + out.println("\t\t State: " + op.stateValues); } + } + out.println(); + } + } + + public static void printAligned(PrintStream out, LIR lir, BlockMap> instructions) { + int longestRAVInstruction = 0; + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); - if (!blockHeaderSB.isEmpty()) { - blockHeaderSB.setLength(blockHeaderSB.length() - 2); + for (var instruction : instructions.get(block)) { + int instructionLength = instruction.toString().length(); + if (instructionLength > longestRAVInstruction) { + longestRAVInstruction = instruction.toString().length(); } } + } - if (block.getSuccessorCount() > 0) { - blockHeaderSB.append(" -> "); - for (int i = 0; i < block.getSuccessorCount(); i++) { - blockHeaderSB.append(block.getSuccessorAt(i)).append(", "); - } + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + + printBlockHeader(out, block); + for (var instruction : instructions.get(block)) { + var instructionString = instruction.toString(); + var difference = longestRAVInstruction - instructionString.length(); + + var space = new String(new char[difference + 4]).replace("\0", " "); - if (block.getSuccessorCount() > 0 && !blockHeaderSB.isEmpty()) { - blockHeaderSB.setLength(blockHeaderSB.length() - 2); + out.println("\t" + instructionString + space + instruction.lirInstruction.toString()); + if (instruction instanceof RAVInstruction.Op op && op.stateValues.count > 0) { + out.println("\t\t State: " + op.stateValues); } } + out.println(); + } + } + + public static void printNumbered(PrintStream out, LIR lir, BlockMap> instructions) { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); - out.println(blockHeaderSB); + printBlockHeader(out, block); + int n = 1; for (var instruction : instructions.get(block)) { - out.println("\t" + instruction.toString() + " | " + instruction.getLIRInstruction().toString()); + out.println("\t" + n + "." + instruction.toString()); if (instruction instanceof RAVInstruction.Op op && op.stateValues.count > 0) { out.println("\t\t State: " + op.stateValues); } + + n++; + } + + out.println(); + + n = 1; + for (var instruction : instructions.get(block)) { + out.println("\t" + n + "." + instruction.lirInstruction.toString()); + n++; } out.println(); } } + + protected static void printBlockHeader(PrintStream out, BasicBlock block) { + var blockHeaderSB = new StringBuilder(); + blockHeaderSB.append(block.toString()).append(": "); + + if (block.getPredecessorCount() > 0) { + blockHeaderSB.append(" <- "); + for (int i = 0; i < block.getPredecessorCount(); i++) { + blockHeaderSB.append(block.getPredecessorAt(i)).append(", "); + } + + if (!blockHeaderSB.isEmpty()) { + blockHeaderSB.setLength(blockHeaderSB.length() - 2); + } + } + + if (block.getSuccessorCount() > 0) { + blockHeaderSB.append(" -> "); + for (int i = 0; i < block.getSuccessorCount(); i++) { + blockHeaderSB.append(block.getSuccessorAt(i)).append(", "); + } + + if (block.getSuccessorCount() > 0 && !blockHeaderSB.isEmpty()) { + blockHeaderSB.setLength(blockHeaderSB.length() - 2); + } + } + + out.println(blockHeaderSB); + } } From d11b1006732f90d1e7d311ee8bdd96518dc7bc5a Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 7 Mar 2026 14:23:55 +0100 Subject: [PATCH 064/112] Check for java kinds from frames --- .../alloc/verifier/BlockVerifierState.java | 44 +++++++++++++++- .../lir/alloc/verifier/RAVInstruction.java | 24 +++++++++ .../compiler/lir/alloc/verifier/RAValue.java | 1 - .../alloc/verifier/RegAllocVerifierPhase.java | 50 +++++++++++++++++-- 4 files changed, 114 insertions(+), 5 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index c654409dc673..27b604a094cb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -32,6 +32,8 @@ import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; @@ -58,7 +60,7 @@ public class BlockVerifierState { protected ConflictResolver conflictConstantResolver; /** - * Block this state pertains to + * Block this state pertains to. */ protected BasicBlock block; @@ -285,6 +287,46 @@ public void check(RAVInstruction.Base instruction, BasicBlock block) { checkOperandFlags(op.uses, op, block); checkOperandFlags(op.alive, op, block); checkOperandFlags(op.temp, op, block); + + checkBytecodeFrames(op); + } + } + + public void checkBytecodeFrames(RAVInstruction.Op op) { + if (op.bcFrames.isEmpty()) { + return; + } + + for (var frame : op.bcFrames) { + for (int i = 0; i < frame.kinds.length; i++) { + var origJV = frame.orig[i]; + if (!(origJV instanceof AllocatableValue orig) || Value.ILLEGAL.equals(orig)) { + continue; + } + + var currJV = frame.curr[i]; + if (!(currJV instanceof AllocatableValue curr) || Value.ILLEGAL.equals(curr)) { + continue; + } + + var kind = frame.kinds[i]; + + var origLIRKind = orig.getValueKind(LIRKind.class); + var currLIRKind = curr.getValueKind(LIRKind.class); + if (JavaKind.Object.equals(kind)) { + if (!origLIRKind.isValue() && !currLIRKind.isValue()) { + continue; + } + + throw new RAVException(orig + " -> " + curr + " not an object java kind when marked as a reference"); + } else { + if (origLIRKind.isValue() && currLIRKind.isValue()) { + continue; + } + + throw new RAVException(orig + " -> " + curr + " is a reference when not marked as an object java kind"); + } + } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 3a2faf08608f..4f05d6cc0d57 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -35,6 +35,8 @@ import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaValue; import jdk.vm.ci.meta.Value; import java.util.ArrayList; @@ -219,6 +221,21 @@ public String toString() { } } + public static class StateValuePair { + public JavaKind[] kinds; + public JavaValue[] curr; + public JavaValue[] orig; + + StateValuePair(JavaValue[] orig, JavaKind[] kinds) { + this.orig = orig; + this.kinds = kinds; + } + + public void setCurr(JavaValue[] curr) { + this.curr = curr; + } + } + /** * RAV instruction that handles a regular operation * in an abstract way - we do not care about the function of said operation. @@ -249,6 +266,11 @@ public static class Op extends Base { */ public ValueArrayPair stateValues; + /** + * Bytecode frame information for this instruction. + */ + public ArrayList bcFrames; + /** * Count number of values stored. */ @@ -290,6 +312,8 @@ public Op(LIRInstruction instruction) { instruction.forEachState(countValuesProc); this.stateValues = new ValueArrayPair(countValuesProc.getCount()); + + this.bcFrames = new ArrayList<>(); } public boolean hasMissingDefinitions() { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index 1ff31a78e981..3966e9827225 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -26,7 +26,6 @@ import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index bf4e0c16f0cc..4705a1aad3c6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -32,9 +32,11 @@ import jdk.graal.compiler.debug.TimerKey; 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.StandardOp; +import jdk.graal.compiler.lir.StateProcedure; import jdk.graal.compiler.lir.alloc.RegisterAllocationPhase; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.LIRPhase; @@ -43,6 +45,7 @@ import jdk.graal.compiler.options.OptionType; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; +import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.code.ValueUtil; import org.graalvm.collections.Equivalence; @@ -91,7 +94,7 @@ public RegAllocVerifierPhase(RegisterAllocationPhase allocator, LIRPhase getStackSlotAllocator() { } /** - * Set stack allocator, used by tests + * Set stack allocator, used by tests. * * @param stackSlotAllocator Stack allocator */ @@ -145,7 +148,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo stackSlotAllocator.apply(target, lirGenRes, context); } - try (final DebugCloseable t = VerifierTimer.start(lir.getDebug())) { + try (DebugCloseable t = VerifierTimer.start(lir.getDebug())) { verifyAllocation(lir, preAllocMap, context); } @@ -209,6 +212,27 @@ protected Map saveInstructionsPreAlloc(LIR instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); instruction.forEachState(opRAVInstr.stateValues.copyOriginalProc); + instruction.forEachState(new StateProcedure() { + @Override + public void doState(LIRFrameState state) { + if (state.topFrame == null) { + return; + } + + BytecodeFrame frame = state.topFrame; + while (frame != null) { + var kinds = frame.getSlotKinds(); + if (kinds.length != frame.numLocals + frame.numStack) { + frame = frame.caller(); + continue; + } + + var values = frame.values.clone(); + opRAVInstr.bcFrames.add(new RAVInstruction.StateValuePair(values, kinds)); + frame = frame.caller(); + } + } + }); preallocMap.put(instruction, opRAVInstr); @@ -306,6 +330,26 @@ protected BlockMap> getVerifierInstructions(LIR lir, M instruction.forEachTemp(opRAVInstr.temp.copyCurrentProc); instruction.forEachAlive(opRAVInstr.alive.copyCurrentProc); instruction.forEachState(opRAVInstr.stateValues.copyCurrentProc); + instruction.forEachState(new StateProcedure() { + @Override + public void doState(LIRFrameState state) { + if (state.topFrame == null) { + return; + } + + int i = 0; + BytecodeFrame frame = state.topFrame; + while (frame != null) { + if (i >= opRAVInstr.bcFrames.size()) { + break; + } + + opRAVInstr.bcFrames.get(i).setCurr(frame.values); + frame = frame.caller(); + i++; + } + } + }); instructionList.add(opRAVInstr); var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); From e0a266ee5ddffe4d7a5538b1c1f81966c248a143 Mon Sep 17 00:00:00 2001 From: glencoco Date: Thu, 12 Mar 2026 11:20:56 +0100 Subject: [PATCH 065/112] Remove block argument in block state --- .../alloc/verifier/BlockVerifierState.java | 58 ++++++++----------- .../lir/alloc/verifier/RegAllocVerifier.java | 10 ++-- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 27b604a094cb..bff457d18a19 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -37,8 +37,6 @@ import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; -import java.util.List; - /** * Verification state a block is in. */ @@ -100,16 +98,15 @@ public boolean meetWith(BlockVerifierState other) { * after stack allocation, because some values are no longer present. * * @param op Operation for which we are checking state values - * @param block Block where this operation is located */ - protected void checkStateValues(RAVInstruction.Op op, BasicBlock block) { + protected void checkStateValues(RAVInstruction.Op op) { if (!op.hasCompleteState()) { // Some values are null after allocation because of stack slot allocator // because it is skipped when iteration (StackLockValue). return; } - checkInputs(op.stateValues, op, block); + checkInputs(op.stateValues, op); } /** @@ -118,12 +115,11 @@ protected void checkStateValues(RAVInstruction.Op op, BasicBlock block) { * * @param values Array of pairs of current location and original variable. * @param op Operation this input array of values belongs to - * @param block Block this operation is in */ - protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { + protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op) { // Check that incoming values are not unknown or conflicted - these only matter if used for (int idx = 0; idx < values.count; idx++) { - checkOperand(values.orig[idx], values.curr[idx], op, block); + checkOperand(values.orig[idx], values.curr[idx], op); } } @@ -138,9 +134,8 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. * @param orig Original variable * @param curr Current location * @param op Operation where these are used - * @param block Block where this operation is */ - protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op, BasicBlock block) { + protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { assert orig != null; if (curr == null) { @@ -272,21 +267,20 @@ protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { * check out to the state stored in for this block. * * @param instruction Instruction we are checking - * @param block Block where it is located */ - public void check(RAVInstruction.Base instruction, BasicBlock block) { + public void check(RAVInstruction.Base instruction) { if (instruction instanceof RAVInstruction.Op op) { - checkInputs(op.uses, op, block); - checkInputs(op.alive, op, block); - checkStateValues(op, block); + checkInputs(op.uses, op); + checkInputs(op.alive, op); + checkStateValues(op); - checkTempKind(op, block); - checkAliveConstraint(op, block); + checkTempKind(op); + checkAliveConstraint(op); - checkOperandFlags(op.dests, op, block); - checkOperandFlags(op.uses, op, block); - checkOperandFlags(op.alive, op, block); - checkOperandFlags(op.temp, op, block); + checkOperandFlags(op.dests, op); + checkOperandFlags(op.uses, op); + checkOperandFlags(op.alive, op); + checkOperandFlags(op.temp, op); checkBytecodeFrames(op); } @@ -335,10 +329,9 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { * original variables with after allocation concrete locations. * * @param op Instruction we update state from - * @param block Block where it is located * @throws KindsMismatchException if a pair does not match */ - protected void checkTempKind(RAVInstruction.Op op, BasicBlock block) { + protected void checkTempKind(RAVInstruction.Op op) { for (int i = 0; i < op.temp.count; i++) { var curr = op.temp.curr[i]; var orig = op.temp.orig[i]; @@ -357,9 +350,8 @@ protected void checkTempKind(RAVInstruction.Op op, BasicBlock block) { * is complete, but is used either as an output or a generic input. * * @param instruction Instruction with alive inputs - * @param block Block this instruction is in */ - protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock block) { + protected void checkAliveConstraint(RAVInstruction.Op instruction) { for (int i = 0; i < instruction.alive.count; i++) { RAValue value = instruction.alive.curr[i]; if (value == null) { @@ -392,10 +384,9 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction, BasicBlock * * @param values Value array pair we are verifying * @param op Instruction which holds this array, for tracing in exceptions - * @param block Block this instruction is in, for tracing in exceptions * @throws OperandFlagMismatchException Operand is a wrong type based on OperandFlag set. */ - protected void checkOperandFlags(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { + protected void checkOperandFlags(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op) { for (int i = 0; i < values.count; i++) { var curr = values.curr[i]; if (curr == null) { @@ -432,12 +423,11 @@ protected void checkOperandFlags(RAVInstruction.ValueArrayPair values, RAVInstru * the symbol that was present before allocation was completed. * * @param instruction Instruction we update state from - * @param block Block where it is located */ - public void update(RAVInstruction.Base instruction, BasicBlock block) { + public void update(RAVInstruction.Base instruction) { switch (instruction) { - case RAVInstruction.Op op -> this.updateWithOp(op, block); - case RAVInstruction.ValueMove virtMove -> this.updateWithValueMove(virtMove, block); + case RAVInstruction.Op op -> this.updateWithOp(op); + case RAVInstruction.ValueMove virtMove -> this.updateWithValueMove(virtMove); case RAVInstruction.LocationMove move -> this.values.putClone(move.to, this.values.get(move.from)); default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } @@ -450,9 +440,8 @@ public void update(RAVInstruction.Base instruction, BasicBlock block) { * allocation. * * @param op Operation we update state from - * @param block Block where it is located */ - protected void updateWithOp(RAVInstruction.Op op, BasicBlock block) { + protected void updateWithOp(RAVInstruction.Op op) { for (int i = 0; i < op.dests.count; i++) { if (op.dests.orig[i].isIllegal()) { continue; // Safe to ignore, when destination is illegal value, not when used. @@ -586,9 +575,8 @@ protected void checkCalleeSavedRegisters() { * to the new variable. * * @param valueMove Value move we update state from - * @param block Block where it is located */ - protected void updateWithValueMove(RAVInstruction.ValueMove valueMove, BasicBlock block) { + protected void updateWithValueMove(RAVInstruction.ValueMove valueMove) { if (valueMove.getLocation().isVariable()) { // Whenever there is a move between two variables, // we need to change every location containing the old variable (rhs - source) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 614d0230e9be..7daef2d30c3f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -112,7 +112,7 @@ public void calculateEntryBlocks() { // Create new entry state for successor blocks out of current block state var state = new BlockVerifierState(block, this.blockEntryStates.get(block)); for (var instr : instructions) { - state.update(instr, block); + state.update(instr); } for (int i = 0; i < block.getSuccessorCount(); i++) { @@ -153,8 +153,8 @@ public void verifyInstructionInputs() { var instructions = this.blockInstructions.get(block); for (var instr : instructions) { - state.check(instr, block); - state.update(instr, block); + state.check(instr); + state.update(instr); } if (block.getSuccessorCount() == 0) { @@ -177,8 +177,8 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { for (var instr : instructions) { try { - state.check(instr, block); - state.update(instr, block); + state.check(instr); + state.update(instr); } catch (RAVException e) { exceptions.add(e); } From 9d641daf5ccb91c56bc2153556eb55bb7915f5e7 Mon Sep 17 00:00:00 2001 From: glencoco Date: Thu, 12 Mar 2026 11:22:55 +0100 Subject: [PATCH 066/112] Add references list --- .../alloc/verifier/BlockVerifierState.java | 51 ++++++++++++++----- .../lir/alloc/verifier/RAVInstruction.java | 9 +++- .../lir/alloc/verifier/VerifierPrinter.java | 11 +++- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index bff457d18a19..e44a5f245bb6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -283,14 +283,14 @@ public void check(RAVInstruction.Base instruction) { checkOperandFlags(op.temp, op); checkBytecodeFrames(op); + + if (op.references != null) { + checkReferences(op); + } } } public void checkBytecodeFrames(RAVInstruction.Op op) { - if (op.bcFrames.isEmpty()) { - return; - } - for (var frame : op.bcFrames) { for (int i = 0; i < frame.kinds.length; i++) { var origJV = frame.orig[i]; @@ -317,7 +317,8 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { if (origLIRKind.isValue() && currLIRKind.isValue()) { continue; } - + // PointerTrackingTest + // jdk.graal.compiler.replacements.test.DerivedOopTest throw new RAVException(orig + " -> " + curr + " is a reference when not marked as an object java kind"); } } @@ -479,6 +480,10 @@ protected void updateWithOp(RAVInstruction.Op op) { RAValue location = op.temp.curr[i]; this.values.put(location, UnknownAllocationState.INSTANCE); } + + if (op.references != null) { + updateWithSafePoint(op); + } } /** @@ -486,13 +491,11 @@ protected void updateWithOp(RAVInstruction.Op op) { * any other references not included in said list are to be set as unknown so * there's no freed pointer use. * - * Not in use currently, because we do not have a reliable list of live references. + * References need to be retrieved using LocationMarker classes. * - * Reference test case: AOTTutorial - * - * @param references List of references deemed as live by the GC + * @param op SafePoint we are using to remove old references */ - protected void updateWithSafePoint(List references) { + protected void updateWithSafePoint(RAVInstruction.Op op) { for (var entry : this.values.internalMap.entrySet()) { var state = entry.getValue(); if (state.isUnknown() || state.isConflicted()) { @@ -500,12 +503,12 @@ protected void updateWithSafePoint(List references) { } var valueAllocState = (ValueAllocationState) state; - if (valueAllocState.getValue().getValueKind(LIRKind.class).isValue()) { + if (Value.ILLEGAL.equals(valueAllocState.getValue()) || valueAllocState.getValue().getValueKind(LIRKind.class).isValue()) { continue; // Not a reference, continue } boolean referenceFound = false; - for (var reference : references) { + for (RAValue reference : op.references) { if (reference.equals(entry.getKey())) { referenceFound = true; break; @@ -520,7 +523,29 @@ protected void updateWithSafePoint(List references) { // maybe it makes sense to keep registers that have live references, // that are same as the one in references list? Because said list // is expected to have stack slots and registers can retain same references. - this.values.put(entry.getKey(), UnknownAllocationState.INSTANCE); + entry.setValue(new ValueAllocationState(new RAValue(Value.ILLEGAL), op, block)); + } + } + + /** + * Check if all values defined in references list are not + * unknown in the allocation state map. + * + * @param op Operation which holds these references + */ + protected void checkReferences(RAVInstruction.Op op) { + for (RAValue reference : op.references) { + var state = values.get(reference); + if (state.isConflicted()) { + continue; + } + + if (state.isUnknown()) { + throw new RAVException("No reference inside " + reference + " in " + op + " in " + block); + } + + // There are cases of references not having kind set as reference + // so checking for references here would throw an exception } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 4f05d6cc0d57..ad6ddff82644 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.hotspot.aarch64.AArch64HotSpotSafepointOp; import jdk.graal.compiler.hotspot.amd64.AMD64HotSpotSafepointOp; import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; @@ -271,6 +272,12 @@ public static class Op extends Base { */ public ArrayList bcFrames; + /** + * List of GC roots, calculated using LocationMarker class, + * other references in state maps need to be nullified. + */ + public List references; + /** * Count number of values stored. */ @@ -338,7 +345,7 @@ public boolean isCall() { } public boolean isSafePoint() { - return lirInstruction instanceof AMD64HotSpotSafepointOp; + return references != null && (lirInstruction instanceof AMD64HotSpotSafepointOp || lirInstruction instanceof AArch64HotSpotSafepointOp || isCall()); } /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index 417779bbaa3d..42fd1d8e2d0d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -79,9 +79,16 @@ public static void printAligned(PrintStream out, LIR lir, BlockMap 0) { - out.println("\t\t State: " + op.stateValues); + if (instruction instanceof RAVInstruction.Op op) { + if (op.lirInstruction.hasState()) { + out.println("\t\t State: " + op.stateValues); + } + + if (op.references != null) { + out.println("\t\t References: " + op.references); + } } + } out.println(); } From 225c9a06c0293865c7436477f684c0cba517171c Mon Sep 17 00:00:00 2001 From: glencoco Date: Fri, 13 Mar 2026 13:26:17 +0100 Subject: [PATCH 067/112] Overwrite virtual stack slot in ValueMove --- .../lir/alloc/verifier/RAVInstruction.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index ad6ddff82644..ac59c67e8b05 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -498,11 +498,18 @@ public void setLocation(RAValue location) { */ public RAValue getLocation() { if (LIRValueUtil.isVirtualStackSlot(location.getValue())) { - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(lirInstruction); - var input = valueMov.getInput(); - if (ValueUtil.isStackSlot(input)) { + Value moveLocation; + if (StandardOp.LoadConstantOp.isLoadConstantOp(lirInstruction)) { + var loadConstOp = StandardOp.LoadConstantOp.asLoadConstantOp(lirInstruction); + moveLocation = loadConstOp.getResult(); + } else { + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(lirInstruction); + moveLocation = valueMov.getInput(); + } + + if (ValueUtil.isStackSlot(moveLocation)) { // Change vstack to allocated stack slot, because it was changed - location.value = input; + location.value = moveLocation; } } From 362858d2acc681128f3f5e6b7c711538aea38ae8 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 14 Mar 2026 11:32:30 +0100 Subject: [PATCH 068/112] Update backup stack slot of a stack move --- .../alloc/verifier/BlockVerifierState.java | 56 +++++++++---------- .../lir/alloc/verifier/RAVInstruction.java | 14 ++++- .../alloc/verifier/RegAllocVerifierPhase.java | 6 ++ .../verifier/ValueNotInRegisterException.java | 2 +- 4 files changed, 45 insertions(+), 33 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index e44a5f245bb6..d96f65865fa8 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -38,7 +38,10 @@ import jdk.vm.ci.meta.ValueKind; /** - * Verification state a block is in. + * Verification state a block is in, holds a mapping + * between locations and their allocation states, which + * can be unknown, value - it's contents or + * conflicted - multiple values conflict with each other. */ public class BlockVerifierState { /** @@ -284,9 +287,13 @@ public void check(RAVInstruction.Base instruction) { checkBytecodeFrames(op); - if (op.references != null) { - checkReferences(op); - } + // We do not check the list of references if the state map + // actually has a reference inside it for said location, + // because there seems to be more cases of that simply + // not being true, either backup slots in stack moves + // are not yet defined / have different lir kind + // or the not marked as a reference - usually related to + // rbp in stack. } } @@ -429,7 +436,14 @@ public void update(RAVInstruction.Base instruction) { switch (instruction) { case RAVInstruction.Op op -> this.updateWithOp(op); case RAVInstruction.ValueMove virtMove -> this.updateWithValueMove(virtMove); - case RAVInstruction.LocationMove move -> this.values.putClone(move.to, this.values.get(move.from)); + case RAVInstruction.LocationMove move -> { + if (move instanceof RAVInstruction.StackMove stackMove) { + // Maybe the backup slot should hold what the scratch register holds? + this.values.put(stackMove.backupSlot, UnknownAllocationState.INSTANCE); + } + + this.values.putClone(move.to, this.values.get(move.from)); + } default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } } @@ -443,6 +457,12 @@ public void update(RAVInstruction.Base instruction) { * @param op Operation we update state from */ protected void updateWithOp(RAVInstruction.Op op) { + if (op.references != null) { + // First we remove unknown references + // then we define new values by the return value + updateWithSafePoint(op); + } + for (int i = 0; i < op.dests.count; i++) { if (op.dests.orig[i].isIllegal()) { continue; // Safe to ignore, when destination is illegal value, not when used. @@ -480,10 +500,6 @@ protected void updateWithOp(RAVInstruction.Op op) { RAValue location = op.temp.curr[i]; this.values.put(location, UnknownAllocationState.INSTANCE); } - - if (op.references != null) { - updateWithSafePoint(op); - } } /** @@ -527,28 +543,6 @@ protected void updateWithSafePoint(RAVInstruction.Op op) { } } - /** - * Check if all values defined in references list are not - * unknown in the allocation state map. - * - * @param op Operation which holds these references - */ - protected void checkReferences(RAVInstruction.Op op) { - for (RAValue reference : op.references) { - var state = values.get(reference); - if (state.isConflicted()) { - continue; - } - - if (state.isUnknown()) { - throw new RAVException("No reference inside " + reference + " in " + op + " in " + block); - } - - // There are cases of references not having kind set as reference - // so checking for references here would throw an exception - } - } - /** * Take list of callee saved registers and add them to the start * block state with their own values as symbols in order to check diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index ac59c67e8b05..ce12be0926fc 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -368,7 +368,12 @@ public boolean hasCompleteState() { @Override public String toString() { - return this.dests.toString() + " = Op " + this.uses.toString() + " " + this.alive.toString() + " " + this.temp.toString(); + var opString = lirInstruction.name(); + if (opString.isEmpty()) { + opString = "OP"; + } + + return this.dests.toString() + " = " + opString + " " + this.uses.toString() + " " + this.alive.toString() + " " + this.temp.toString(); } } @@ -416,7 +421,13 @@ public static class StackMove extends LocationMove { public RAValue from; public RAValue to; + public RAValue backupSlot; + public StackMove(LIRInstruction instr, Value from, Value to) { + this(instr, from, to, null); + } + + public StackMove(LIRInstruction instr, Value from, Value to, Value backupSlot) { super(instr, from, to); assert from instanceof StackSlot || from instanceof VirtualStackSlot : "StackMove needs to receive instanceof StackSlot or VirtualStackSlot"; @@ -424,6 +435,7 @@ public StackMove(LIRInstruction instr, Value from, Value to) { this.from = RAValue.create(from); this.to = RAValue.create(to); + this.backupSlot = RAValue.create(backupSlot); } public String toString() { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 4705a1aad3c6..deda29dd4d71 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -38,6 +38,7 @@ import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.StateProcedure; import jdk.graal.compiler.lir.alloc.RegisterAllocationPhase; +import jdk.graal.compiler.lir.amd64.AMD64Move; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.LIRPhase; import jdk.graal.compiler.options.Option; @@ -479,6 +480,11 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) } else if (ValueUtil.isRegister(input) && ValueUtil.isRegister(result)) { return new RAVInstruction.RegMove(instruction, ValueUtil.asRegisterValue(input), ValueUtil.asRegisterValue(result)); } else if (LIRValueUtil.isStackSlotValue(input) && LIRValueUtil.isStackSlotValue(result)) { + if (valueMov instanceof AMD64Move.AMD64StackMove stackMove) { + // Cannot access the isScratchAlwaysZero to see if backup slot is used + return new RAVInstruction.StackMove(instruction, input, result, stackMove.getBackupSlot()); + } + return new RAVInstruction.StackMove(instruction, input, result); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 2c86627c11be..f630fc470131 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -77,7 +77,7 @@ static String getErrorMessage(LIRInstruction instruction, BasicBlock block, R messageBuilder.append("\n"); for (var conflictedState : confStates) { - messageBuilder.append(" - ").append(conflictedState.getRAValue()).append(" from ").append(conflictedState.source).append("\n"); + messageBuilder.append(" - ").append(conflictedState.getRAValue()).append(" from ").append(conflictedState.source).append(" in ").append(conflictedState.block).append("\n"); } } else { messageBuilder.append(state); From 49baaa7b9fd1fdf17ad4818269943d7edc016f5e Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 14 Mar 2026 14:24:27 +0100 Subject: [PATCH 069/112] Fix issues when resolving variables --- .../verifier/FromUsageResolverGlobal.java | 181 ++++++++++++++---- 1 file changed, 147 insertions(+), 34 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 7a355cae6379..1e9a2c5d0e07 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -40,25 +40,98 @@ import java.util.Set; /** - * Resolve label variable locations based on their first usage, - * globally - spanning over all program blocks for every variable. + * Resolve variables phi variables back to labels + * and jumps by find their first usage and handling + * any reg allocator inserted moves back to the defining + * label. + * + * Register allocator strips us of this information that is + * necessary for the verification. In order to avoid modifying + * the existing allocators, we rather try to resolve this + * information from other instructions. + * + * Variables with only usage in jump instructions are + * marked as aliases and are resolved after their successors. + * + * If a variable has no usage, then no location is resolved + * and verification continues without issues. + * + * In the case the first usage of a label-defined variable + * is wrong, then JUMP instructions from predecessors + * fail the verification - wrong register will be chosen. */ public class FromUsageResolverGlobal { + /** + * LIR of the compilation unit we are resolving for. + */ protected LIR lir; + + /** + * Verifier IR. + */ protected BlockMap> blockInstructions; + /** + * Mapping of variables to the labels that defined them. + */ public Map labelMap; + + /** + * Is this a label-defined variable? + */ public Map defined; + + /** + * Was this label-defined variable reached? + */ public Map reached; + + /** + * Mapping of operation to a set of variable for which + * this operation is a first usage. + */ public Map> firstUsages; + + /** + * Initial locations of label-defined variables + * to set them to when their first usage is found. + */ public Map initialLocations; - public Map> aliasMap; - public Map> aliasBlockMap; - public BlockMap blockUsageMap; // Entry blocks! - public List> endBlocks; + /** + * Variable and a block where it's coming from (last jump instruction), + * this variable is aliased by the succeeding label, which needs to + * be resolved first, before this one can be. + */ + class AliasPair { + RAVariable variable; + BasicBlock block; + + AliasPair(RAVariable variable, BasicBlock block) { + this.variable = variable; + this.block = block; + } + } + + /** + * Map of variables are aliases for a list of variables + * used in predecessor jump instructions. First the + * successor label variable needs to be resolved and after + * the predecessor labels. + */ + private Map> aliasMap; + + /** + * Block map of their usages objects. + */ + public BlockMap blockUsageMap; + + /** + * Set of blocks that have no successors. + */ + public Set> endBlocks; - final class BlockUsage { + protected final class BlockUsage { private final Set reached; private final Map locations; @@ -74,13 +147,9 @@ private BlockUsage(BlockUsage blockDefs) { private BlockUsage merge(BlockUsage other) { var newDefs = new BlockUsage(this); - for (var defValue : other.reached) { - newDefs.reached.add(defValue); - } + newDefs.reached.addAll(other.reached); - var iterator = other.locations.keySet().iterator(); - while (iterator.hasNext()) { - var variable = iterator.next(); + for (RAVariable variable : other.locations.keySet()) { var defValue = other.locations.get(variable); if (defValue == null) { continue; @@ -108,14 +177,16 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b this.initialLocations = new EconomicHashMap<>(); this.aliasMap = new EconomicHashMap<>(); this.aliasMap = new EconomicHashMap<>(); - this.aliasBlockMap = new EconomicHashMap<>(); - this.endBlocks = new ArrayList<>(); + this.endBlocks = new EconomicHashSet<>(); this.blockUsageMap = new BlockMap<>(lir.getControlFlowGraph()); } /** - * Resolves label variable registers by finding where they are used. + * Resolves label variable locations by finding where they are first used. + * Walk back from their usage to their defining label (bottom-up), handling any + * spills, reloads and moves along the way to set the location in label back + * after register allocator strips this information. */ public void resolvePhiFromUsage() { Queue> worklist = new ArrayDeque<>(); @@ -176,6 +247,10 @@ public void resolvePhiFromUsage() { } } + /** + * Initialize first usages for variables, top-down in-order to + * collect all necessary information for the resolution. + */ protected void initializeUsages() { Queue> worklist = new ArrayDeque<>(); @@ -197,6 +272,17 @@ protected void initializeUsages() { for (var i = 0; i < label.dests.count; i++) { if (label.dests.orig[i].isVariable()) { + if (label.dests.curr[i] != null) { + // TestCase: TruffleSafepointTest + // java.concurrent.ForkJoinPool + // some methods for this class have location kept in + // them after the register allocation is complete + // but such information should be stripped by the allocator. + // This information uses one register for 2 variables in a label + // and triggers an error in the verification + label.dests.curr[i] = null; + } + var variable = label.dests.orig[i].asVariable(); defined.put(variable, true); labelMap.put(variable, label); @@ -234,10 +320,9 @@ protected void initializeUsages() { continue; // Loop } - aliasBlockMap.put(variable, block); - var aliasedVariables = aliasMap.getOrDefault(alias, new ArrayList<>()); - aliasedVariables.add(variable); + aliasedVariables.add(new AliasPair(variable, block)); + aliasMap.put(alias, aliasedVariables); } } @@ -256,6 +341,13 @@ protected void initializeUsages() { } } + /** + * Find first usages for variables defined in labels. + * + * @param values Values of this instruction where are looking for usage + * @param op Instruction that holds values + * @param block Block where this instruction is in + */ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { for (var i = 0; i < values.count; i++) { if (!values.orig[i].isVariable()) { @@ -277,12 +369,27 @@ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction for (var entry : aliasMap.entrySet()) { var aliasedVariables = entry.getValue(); - aliasedVariables.remove(variable); + + aliasedVariables.removeIf(pair -> pair.variable.equals(variable)); } } } } + /** + * Handle a register allocator inserted move, change + * locations of variables based on the locations. + * + * If a variable is in location reg1 and a move + * is found reg1 = MOVE reg2, then said variable + * will now be in reg2, because reg1 will now + * have different content when walking through + * the instructions in reverse. + * + * @param usage Variable locations for this block + * @param from Source location + * @param to Destination location + */ protected void handleMove(BlockUsage usage, RAValue from, RAValue to) { var updatedVariables = new EconomicHashMap(); for (var entry : usage.locations.entrySet()) { @@ -300,6 +407,15 @@ protected void handleMove(BlockUsage usage, RAValue from, RAValue to) { usage.locations.putAll(updatedVariables); } + /** + * Resolve locations for all variables in a label, also + * mark first usage for aliased variables - variables + * used in predecessors that have no other usage. + * + * @param usage usage for the block we are resolving, contains the locations + * @param label label we are resolving + * @param block block of the label + */ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBlock block) { for (int i = 0; i < label.dests.count; i++) { if (!label.dests.orig[i].isVariable()) { @@ -308,11 +424,11 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc var variable = label.dests.orig[i].asVariable(); if (usage.locations.get(variable) == null) { - continue; + continue; // Not resolved yet } if (label.dests.curr[i] != null) { - continue; + continue; // Already resolved } var location = usage.locations.get(variable); @@ -325,24 +441,21 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc var pred = block.getPredecessorAt(j); var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); - jump.alive.curr[i] = location; + jump.alive.curr[i] = location; // Set predecessor location } - // Variables that are passed into jumps without any other usage are aliases - // for same variable in successor label, whenever said variable is resolved - // we now have a location for this variable and can take other moves into account. + // Variables that are passed into jumps without any other usage are resolved + // after variable, it's alias, in successor label. if (aliasMap.containsKey(variable)) { var aliasedVariables = aliasMap.get(variable); - for (var aliases : aliasedVariables) { - var aliasBlock = aliasBlockMap.get(aliases); - - if (blockUsageMap.get(aliasBlock) == null) { - this.blockUsageMap.put(aliasBlock, new BlockUsage()); + for (var aliasPair : aliasedVariables) { + if (blockUsageMap.get(aliasPair.block) == null) { + this.blockUsageMap.put(aliasPair.block, new BlockUsage()); } - var aliasBlockUsage = blockUsageMap.get(aliasBlock); - aliasBlockUsage.locations.put(aliases, location); - aliasBlockUsage.reached.add(aliases); + var aliasBlockUsage = blockUsageMap.get(aliasPair.block); + aliasBlockUsage.locations.put(aliasPair.variable, location); + aliasBlockUsage.reached.add(aliasPair.variable); } } From 664f145cc681286e9b574f2f2649637c6e5443e5 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 17 Mar 2026 16:29:25 +0100 Subject: [PATCH 070/112] Move vstack to stack conversion for value moves --- .../lir/alloc/verifier/RAVInstruction.java | 19 +-------- .../alloc/verifier/RegAllocVerifierPhase.java | 39 ++++++++++++++++++- .../lir/alloc/verifier/VerifierPrinter.java | 20 ++++++++-- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index ce12be0926fc..0ad4b7fa0769 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import java.util.Locale; public class RAVInstruction { /** @@ -373,7 +374,7 @@ public String toString() { opString = "OP"; } - return this.dests.toString() + " = " + opString + " " + this.uses.toString() + " " + this.alive.toString() + " " + this.temp.toString(); + return this.dests.toString() + " = " + opString.toUpperCase(Locale.ROOT) + " " + this.uses.toString() + " " + this.alive.toString() + " " + this.temp.toString(); } } @@ -509,22 +510,6 @@ public void setLocation(RAValue location) { * Where variable (or constant) is being stored. */ public RAValue getLocation() { - if (LIRValueUtil.isVirtualStackSlot(location.getValue())) { - Value moveLocation; - if (StandardOp.LoadConstantOp.isLoadConstantOp(lirInstruction)) { - var loadConstOp = StandardOp.LoadConstantOp.asLoadConstantOp(lirInstruction); - moveLocation = loadConstOp.getResult(); - } else { - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(lirInstruction); - moveLocation = valueMov.getInput(); - } - - if (ValueUtil.isStackSlot(moveLocation)) { - // Change vstack to allocated stack slot, because it was changed - location.value = moveLocation; - } - } - return location; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index deda29dd4d71..c81e7e8018ba 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -49,6 +49,7 @@ import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.Value; import org.graalvm.collections.Equivalence; import java.io.FileNotFoundException; @@ -360,7 +361,9 @@ public void doState(LIRFrameState state) { } var virtualMoves = opRAVInstr.getVirtualMoveList(); - instructionList.addAll(virtualMoves); + for (var virtMove : virtualMoves) { + instructionList.add(fixOldValueMove(virtMove)); + } } blockInstructions.put(block, instructionList); @@ -369,6 +372,38 @@ public void doState(LIRFrameState state) { return blockInstructions; } + /** + * Fixes value move created before any allocation has happened, + * we mainly care about stack allocator running - old value move + * still keeps the virtual stack slot, but the underlying lir + * instruction already has an allocated concrete stack slot, so + * for verification to work correctly, it needs to be changed. + * + * @param valueMove Old value move created before any (stack) allocation + * @return Fixed value move + */ + protected RAVInstruction.ValueMove fixOldValueMove(RAVInstruction.ValueMove valueMove) { + if (!LIRValueUtil.isVirtualStackSlot(valueMove.location.getValue())) { + return valueMove; + } + + Value moveLocation; + if (StandardOp.LoadConstantOp.isLoadConstantOp(valueMove.lirInstruction)) { + var loadConstOp = StandardOp.LoadConstantOp.asLoadConstantOp(valueMove.lirInstruction); + moveLocation = loadConstOp.getResult(); + } else { + var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(valueMove.lirInstruction); + moveLocation = valueMov.getInput(); + } + + if (ValueUtil.isStackSlot(moveLocation)) { + // Change vstack to allocated stack slot, because it was changed + valueMove.location.value = moveLocation; + } + + return valueMove; + } + /** * Iterate over every instruction after allocation save it to a set * to see if speculative moves should be re-added or not and also @@ -443,7 +478,7 @@ protected List handleSpeculativeMoves(RAVInstruction.O continue; } - toAdd.add(speculativeMove); + toAdd.add(fixOldValueMove(speculativeMove)); } return toAdd; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index 42fd1d8e2d0d..c7953db9cd2a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -47,8 +47,14 @@ public static void print(PrintStream out, LIR lir, BlockMap 0) { - out.println("\t\t State: " + op.stateValues); + if (instruction instanceof RAVInstruction.Op op) { + if (op.lirInstruction.hasState()) { + out.println("\t\t State: " + op.stateValues); + } + + if (op.references != null) { + out.println("\t\t References: " + op.references); + } } } out.println(); @@ -102,8 +108,14 @@ public static void printNumbered(PrintStream out, LIR lir, BlockMap 0) { - out.println("\t\t State: " + op.stateValues); + if (instruction instanceof RAVInstruction.Op op) { + if (op.lirInstruction.hasState()) { + out.println("\t\t State: " + op.stateValues); + } + + if (op.references != null) { + out.println("\t\t References: " + op.references); + } } n++; From 6b80ecf1d57ecdc6f98f942ffdaa774ad5fbdc1a Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 17 Mar 2026 18:30:08 +0100 Subject: [PATCH 071/112] Fix location warning in RegAllocVerifierTest --- .../core/test/RegAllocVerifierTest.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index 881c7618dfe7..eadff75e8844 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -46,6 +46,7 @@ import jdk.graal.compiler.lir.alloc.verifier.ValueNotInRegisterException; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.AllocationPhase; +import jdk.graal.compiler.lir.phases.LIRPhase; import jdk.graal.compiler.lir.phases.LIRSuites; import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.util.EconomicHashMap; @@ -82,7 +83,7 @@ public class RegAllocVerifierTest extends GraalCompilerTest { RAVPhaseWrapper phase; class RAVPhaseWrapper extends RegAllocVerifierPhase { - public RAVPhaseWrapper() { + RAVPhaseWrapper() { super(null, null); } @@ -304,7 +305,7 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet getVariablesFromVirtualMoves(LIR lir, BasicBl for (var move : op.getVirtualMoveList()) { if (move.variableOrConstant.isVariable()) { - variables.put(move.variableOrConstant.asVariable(), move.location); + variables.put(move.variableOrConstant.asVariable(), move.getLocation()); } } } @@ -725,6 +726,16 @@ protected LIRSuites createLIRSuitesWithVerifier(OptionValues options) { var stage = suites.getAllocationStage(); if (RegAllocVerifierPhase.Options.EnableRAVerifier.getValue(options)) { + var verifier = (RegAllocVerifierPhase) stage.findPhaseInstance(RegisterAllocationPhase.class); + assert verifier != null; + + var stackAllocator = verifier.getStackSlotAllocator(); + if (stackAllocator != null) { + // Do not use stack allocator + verifier.setStackSlotAllocator(null); + stage.appendPhase(stackAllocator); + } + return suites; } @@ -744,14 +755,23 @@ protected LIRSuites createModifiedVerifierLIRSuites(OptionValues options) { var it = stage.findPhase(RegisterAllocationPhase.class); Assert.assertNotNull(it); + LIRPhase stackAllocator = null; var allocator = (RegisterAllocationPhase) it.previous(); if (allocator instanceof RegAllocVerifierPhase rav) { + stackAllocator = rav.getStackSlotAllocator(); + if (stackAllocator != null) { + rav.setStackSlotAllocator(null); + } + phase.setAllocator(rav.getAllocator()); } else { phase.setAllocator(allocator); } it.set(phase); + if (stackAllocator != null) { + stage.appendPhase(stackAllocator); + } return suites; } From 8b49f4923ddf5e8205e6faf4b74aa13b124e49ea Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 17 Mar 2026 18:37:18 +0100 Subject: [PATCH 072/112] Check move kinds against state value being moved --- .../lir/alloc/verifier/BlockVerifierState.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index d96f65865fa8..cc373820cdcb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -294,6 +294,18 @@ public void check(RAVInstruction.Base instruction) { // are not yet defined / have different lir kind // or the not marked as a reference - usually related to // rbp in stack. + } else if (instruction instanceof RAVInstruction.LocationMove move) { + checkMoveKinds(move); + } + } + + protected void checkMoveKinds(RAVInstruction.LocationMove move) { + AllocationState state = this.values.get(move.from); + if (state instanceof ValueAllocationState valueAllocationState) { + RAValue movedValue = valueAllocationState.getRAValue(); + if (!kindsEqual(movedValue, move.to)) { + throw new KindsMismatchException(move.lirInstruction, block, move.to, movedValue, false); + } } } From c7b1080937b8a01d59d4f7becd16181c72f49540 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 17 Mar 2026 18:39:04 +0100 Subject: [PATCH 073/112] Build references before final stage for verification --- .../lir/alloc/verifier/ReferencesBuilder.java | 177 ++++++++++++++++++ .../alloc/verifier/RegAllocVerifierPhase.java | 10 +- .../compiler/lir/dfa/LocationMarker.java | 2 +- 3 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java new file mode 100644 index 000000000000..1a0645d049fd --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import jdk.graal.compiler.core.common.LIRKind; +import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.LIRFrameState; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.dfa.LocationMarker; +import jdk.graal.compiler.lir.framemap.FrameMap; +import jdk.graal.compiler.lir.util.ValueSet; +import jdk.graal.compiler.util.EconomicHashSet; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterAttributes; +import jdk.vm.ci.meta.Value; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isRegister; +import static jdk.vm.ci.code.ValueUtil.isStackSlot; + +/** + * Build references list for operations that can be + * used to in-validate references that are not part + * of it, to make sure GC-freed references are not + * used further. + * + * It uses the LocationMarker class that is used + * in the FinalCodeAnalysisStage where the actual + * set of references is built, we require this + * information earlier. + */ +public class ReferencesBuilder { + class ReferenceSet extends ValueSet { + protected Set references; + + ReferenceSet() { + references = new EconomicHashSet<>(); + } + + ReferenceSet(ReferenceSet other) { + references = new EconomicHashSet<>(other.references); + } + + @Override + public void put(Value v) { + references.add(RAValue.create(v)); + } + + @Override + public void remove(Value v) { + references.remove(RAValue.create(v)); + } + + @Override + public void putAll(ReferenceSet other) { + references.addAll(other.references); + } + + @Override + public ReferenceSet copy() { + return new ReferenceSet(this); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ReferenceSet other) { + return other.references.equals(references); + } + return false; + } + + @Override + public int hashCode() { + throw new UnsupportedOperationException(); + } + } + + class ReferenceMarker extends LocationMarker { + private final List registerAttributes; + protected Map preAllocMap; + + protected ReferenceMarker(LIR lir, FrameMap frameMap, Map preAllocMap) { + super(lir, frameMap); + this.registerAttributes = frameMap.getRegisterConfig().getAttributesMap(); + this.preAllocMap = preAllocMap; + } + + @Override + protected ReferenceSet newLiveValueSet() { + return new ReferenceSet(); + } + + /** + * Only process values that are registers (that can contain references and are allocatable) + * and stack slots, where kind cannot be Illegal. + *

+ * Take from LocationMarkerPhase.Marker.shouldProcessValue + * + * @param operand Value to be processed + * @return If matches criteria for LocationMarker processing + */ + @Override + protected boolean shouldProcessValue(Value operand) { + if (isRegister(operand)) { + Register reg = asRegister(operand); + if (!reg.mayContainReference() || !attributes(reg).isAllocatable()) { + // register that's not allocatable or not part of the reference map + return false; + } + } else if (!isStackSlot(operand)) { + // neither register nor stack slot + return false; + } + + return !operand.getValueKind().equals(LIRKind.Illegal); + } + + private RegisterAttributes attributes(Register reg) { + return registerAttributes.get(reg.number); + } + + /** + * Set references to the operation, if it can be found in + * the pre-allocation map. + * + * @param instruction LIR instruction that holds these references + * @param info LIR frame state + * @param values Set of references for this instruction + */ + @Override + protected void processState(LIRInstruction instruction, LIRFrameState info, ReferenceSet values) { + if (!preAllocMap.containsKey(instruction)) { + return; + } + + var op = (RAVInstruction.Op) preAllocMap.get(instruction); + op.references = values.references.stream().toList(); + } + } + + /** + * Build references for RAV Op instructions to check GC roots. + * + * @param lir LIR + * @param frameMap Frame map necessary to build references + * @param preAllocMap Map of instructions before allocation + */ + public void build(LIR lir, FrameMap frameMap, Map preAllocMap) { + new ReferenceMarker(lir, frameMap, preAllocMap).build(); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index c81e7e8018ba..873cb57da787 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -74,6 +74,9 @@ public static class Options { @Option(help = "Verify output of stack allocator with register allocator", type = OptionType.Debug) public static final OptionKey VerifyStackAllocator = new OptionKey<>(true); + + @Option(help = "Collect reference map information to verify", type = OptionType.Debug) + public static final OptionKey CollectReferences = new OptionKey<>(false); } /** @@ -143,11 +146,16 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo preAllocMap = saveInstructionsPreAlloc(lir); } - boolean verifyStackAlloc = Options.VerifyStackAllocator.getValue(lirGenRes.getLIR().getOptions()); + boolean verifyStackAlloc = Options.VerifyStackAllocator.getValue(lir.getOptions()); allocator.apply(target, lirGenRes, context); if (stackSlotAllocator != null && verifyStackAlloc) { stackSlotAllocator.apply(target, lirGenRes, context); + + if (Options.CollectReferences.getValue(lir.getOptions())) { + // Frame map is only built after stack allocator has run + new ReferencesBuilder().build(lir, lirGenRes.getFrameMap(), preAllocMap); + } } try (DebugCloseable t = VerifierTimer.start(lir.getDebug())) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/dfa/LocationMarker.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/dfa/LocationMarker.java index f8a8020afa10..4d25165f1ef8 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/dfa/LocationMarker.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/dfa/LocationMarker.java @@ -70,7 +70,7 @@ protected LocationMarker(LIR lir, FrameMap frameMap) { protected abstract void processState(LIRInstruction op, LIRFrameState info, S values); - void build() { + public void build() { BasicBlock[] blocks = lir.getControlFlowGraph().getBlocks(); UniqueWorkList worklist = new UniqueWorkList(blocks.length); for (int i = blocks.length - 1; i >= 0; i--) { From 25779b4c61e1ac9f476a4f8bd44686bdba1a8b59 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 17 Mar 2026 18:39:30 +0100 Subject: [PATCH 074/112] Modify tests to pass verifier --- .../graal/compiler/replacements/test/DerivedOopTest.java | 9 +++++++++ .../compiler/replacements/test/PointerTrackingTest.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/DerivedOopTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/DerivedOopTest.java index f1141b5e6584..0b8eeb4dac3b 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/DerivedOopTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/DerivedOopTest.java @@ -27,6 +27,9 @@ import java.util.Objects; import org.graalvm.word.impl.Word; +import jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase; +import jdk.graal.compiler.lir.phases.LIRSuites; +import jdk.graal.compiler.options.OptionValues; import org.hamcrest.MatcherAssert; import org.hamcrest.core.StringContains; import org.junit.Assert; @@ -104,6 +107,12 @@ public boolean equals(Object obj) { } } + @Override + protected LIRSuites createLIRSuites(OptionValues opts) { + var newOptions = new OptionValues(opts, RegAllocVerifierPhase.Options.EnableRAVerifier, false); + return super.createLIRSuites(newOptions); + } + @Test public void testFieldOffset() { // Run a couple times to encourage objects to move diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/PointerTrackingTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/PointerTrackingTest.java index cc6aa83d4059..13cbe78de8bc 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/PointerTrackingTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/PointerTrackingTest.java @@ -24,6 +24,9 @@ */ package jdk.graal.compiler.replacements.test; +import jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase; +import jdk.graal.compiler.lir.phases.LIRSuites; +import jdk.graal.compiler.options.OptionValues; import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -52,6 +55,12 @@ public void before() { Assume.assumeTrue("doesn't aggressively move objects", runtime.getGarbageCollector() != HotSpotGraalRuntime.HotSpotGC.Shenandoah); } + @Override + protected LIRSuites createLIRSuites(OptionValues opts) { + var newOptions = new OptionValues(opts, RegAllocVerifierPhase.Options.EnableRAVerifier, false); + return super.createLIRSuites(newOptions); + } + @Test public void testTracking() { Result result = executeActual(getResolvedJavaMethod("trackingSnippet"), null); From 12c14aad21086253e009bd3636fafffb66cf6248 Mon Sep 17 00:00:00 2001 From: glencoco Date: Tue, 17 Mar 2026 22:35:48 +0100 Subject: [PATCH 075/112] Add @link to comments --- .../AliveConstraintViolationException.java | 4 +- .../lir/alloc/verifier/AllocationState.java | 14 ++-- .../alloc/verifier/AllocationStateMap.java | 15 +++-- .../alloc/verifier/BlockVerifierState.java | 64 +++++++++++++------ .../lir/alloc/verifier/ConflictResolver.java | 15 +++-- .../verifier/ConflictedAllocationState.java | 14 ++-- ...nstantMaterializationConflictResolver.java | 23 +++---- .../verifier/FromUsageResolverGlobal.java | 4 +- .../InvalidRegisterUsedException.java | 2 +- .../OperandFlagMismatchException.java | 2 +- .../lir/alloc/verifier/RARegister.java | 4 ++ .../compiler/lir/alloc/verifier/RAVError.java | 2 +- .../lir/alloc/verifier/RAVException.java | 4 +- .../RAVFailedVerificationException.java | 4 +- .../compiler/lir/alloc/verifier/RAValue.java | 19 +++--- .../lir/alloc/verifier/RAVariable.java | 7 +- .../lir/alloc/verifier/ReferencesBuilder.java | 12 ++-- .../alloc/verifier/RegAllocVerifierPhase.java | 12 ++-- ...aterializedConstantSourceMissingError.java | 4 +- .../verifier/UnknownAllocationState.java | 6 +- .../verifier/UnknownInstructionError.java | 2 +- .../alloc/verifier/ValueAllocationState.java | 16 ++--- 22 files changed, 146 insertions(+), 103 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index b6a29356506c..3ad79b64be0b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -28,9 +28,9 @@ import jdk.graal.compiler.lir.LIRInstruction; /** - * Violation of the alive arguments occurred, + * Violation of the alive inputs occurred, * same location was marked as alive argument - * as well as input or output. + * as well as temp or output. */ @SuppressWarnings("serial") public class AliveConstraintViolationException extends RAVException { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index eba27c1e4d6f..8057c0e042ad 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -27,13 +27,13 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; /** - * Interface for AllocationState stored in AllocationStateMap, - * describing what state physical location is in. + * Interface for state concrete location is in, + * stored in {@link AllocationStateMap}. */ public abstract class AllocationState { /** * Get the default allocation state for every location, - * instead of null, we have Unknown state. + * instead of null, we have {@link UnknownAllocationState unknown} state. * * @return Default state for every location */ @@ -42,18 +42,18 @@ public static AllocationState getDefault() { } /** - * Shortcut to check if state is Unknown. + * Shortcut to check if state is {@link UnknownAllocationState unknown}. * - * @return Is unknown state + * @return Is {@link UnknownAllocationState unknown state} */ public boolean isUnknown() { return false; } /** - * Shortcut to check if state is ConflictedState. + * Shortcut to check if state is {@link ConflictedAllocationState conflicted}. * - * @return Is ConflictedState + * @return Is {@link ConflictedAllocationState conflicted} */ public boolean isConflicted() { return false; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java index 7a568caf0cb1..8a6eb3ac0fbd 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java @@ -35,12 +35,12 @@ /** * Mapping between a location and allocation state, * that stores one of these: - * - Unknown - our null state, nothing was stored yet - * - Value - symbol that is stored at said location - * - Conflict - set of Values that are supposed to be at same location + * - {@link UnknownAllocationState unknown} - our null state, nothing was stored yet + * - {@link ValueAllocationState value} - symbol that is stored at said location + * - {@link ConflictedAllocationState conflicted} - set of Values that are supposed to be at same location * - * Conflicts are resolved by assigning new Value to same location. - * Otherwise, they cannot be used. Value can store register, stack slot, + * Conflicts are resolved by assigning new {@link ValueAllocationState value} to same location. + * Otherwise, they cannot be used. {@link ValueAllocationState Value} can store register, stack slot, * constant, but most importantly variables used before allocation. These * are what we are checking with the verification process. */ @@ -66,8 +66,8 @@ public AllocationStateMap(BasicBlock block, RegisterAllocationConfig register public AllocationStateMap(BasicBlock block, AllocationStateMap other) { internalMap = new EconomicHashMap<>(other.internalMap); - this.block = block; registerAllocationConfig = other.registerAllocationConfig; + this.block = block; } public boolean has(RAValue key) { @@ -101,8 +101,9 @@ public void put(RAValue key, AllocationState state) { /** * Put a new state for location to the map, * without checking if the register can actually be used. + * *

- * This is useful for registers that are used by the abi + * This is useful for registers that are used by the ABI * in the first label, but can actually never be changed, * like rbp. *

diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index cc373820cdcb..a5feaca4df1b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -100,7 +100,7 @@ public boolean meetWith(BlockVerifierState other) { * except we skip those where the state values are incorrectly stored, * after stack allocation, because some values are no longer present. * - * @param op Operation for which we are checking state values + * @param op Operation for which we are checking state values */ protected void checkStateValues(RAVInstruction.Op op) { if (!op.hasCompleteState()) { @@ -128,15 +128,17 @@ protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction. /** * Check that original variable matches symbol stored at - * the current location in the allocation state map. + * the current location in the {@link AllocationStateMap}. + * *

* We also check that kinds match and possibly * rematerialize variables at this point in the * state map. + *

* - * @param orig Original variable - * @param curr Current location - * @param op Operation where these are used + * @param orig Original variable + * @param curr Current location + * @param op Operation where these are used */ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { assert orig != null; @@ -224,7 +226,7 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } /** - * Are kinds equal even when casting (LIRKindWithCast) is present? + * Are kinds equal even when {@link LIRKindWithCast casting} is present? * * @param orig Original variable * @param curr Current location @@ -246,10 +248,12 @@ protected boolean kindsEqual(RAValue orig, RAValue curr) { } /** - * Are kinds equal even when CastValue is present? + * Are kinds equal even when {@link jdk.graal.compiler.lir.CastValue cast value} is present? + * *

* We need to ignore the cast value because the currently stored * value will not be cast. + *

* * @param orig Original variable * @param fromState Value stored in state of the current location @@ -299,6 +303,12 @@ public void check(RAVInstruction.Base instruction) { } } + /** + * Check if the destination of a move has the correct type to + * store the {@link ValueAllocationState value} + * + * @param move Move between locations, inserted by register allocator + */ protected void checkMoveKinds(RAVInstruction.LocationMove move) { AllocationState state = this.values.get(move.from); if (state instanceof ValueAllocationState valueAllocationState) { @@ -309,6 +319,16 @@ protected void checkMoveKinds(RAVInstruction.LocationMove move) { } } + /** + * Check {@link jdk.vm.ci.code.BytecodeFrame frames}, before and + * after allocation, mainly checking that {@link LIRKind} is a + * reference when {@link JavaKind} is an Object and wise-versa, + * checking that {@link LIRKind} is not a reference when + * {@link JavaKind} is not an object. + * + * @param op Operation holding said frames + * @throws RAVException when a violation occurs + */ public void checkBytecodeFrames(RAVInstruction.Op op) { for (var frame : op.bcFrames) { for (int i = 0; i < frame.kinds.length; i++) { @@ -336,8 +356,11 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { if (origLIRKind.isValue() && currLIRKind.isValue()) { continue; } + // These two tests needed to be modified // PointerTrackingTest // jdk.graal.compiler.replacements.test.DerivedOopTest + // so this verification method doesn't throw an error + // when running with them throw new RAVException(orig + " -> " + curr + " is a reference when not marked as an object java kind"); } } @@ -345,10 +368,10 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { } /** - * Check if kinds in the temporary array match before allocation - * original variables with after allocation concrete locations. + * Check if kinds in the {@link RAVInstruction.Op#temp temporary array} + * match before allocation original variables with after allocation concrete locations. * - * @param op Instruction we update state from + * @param op Instruction we update state from * @throws KindsMismatchException if a pair does not match */ protected void checkTempKind(RAVInstruction.Op op) { @@ -370,6 +393,7 @@ protected void checkTempKind(RAVInstruction.Op op) { * is complete, but is used either as an output or a generic input. * * @param instruction Instruction with alive inputs + * @throws AliveConstraintViolationException throw when violation occurs */ protected void checkAliveConstraint(RAVInstruction.Op instruction) { for (int i = 0; i < instruction.alive.count; i++) { @@ -398,7 +422,7 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction) { /** * Make sure concrete current locations changed by the allocator - * are not violating set of LIRInstruction.OperandFlag flags, + * are not violating set of {@link LIRInstruction.OperandFlag flags}, * which specify what type can they be. This is done on every * array of pairs (dest, uses, alive, temp). * @@ -439,7 +463,7 @@ protected void checkOperandFlags(RAVInstruction.ValueArrayPair values, RAVInstru /** * Update the current state based on outputs of this instruction. - * Setting contents of current location in allocation state map to + * Setting contents of current location in {@link AllocationStateMap} to * the symbol that was present before allocation was completed. * * @param instruction Instruction we update state from @@ -462,11 +486,11 @@ public void update(RAVInstruction.Base instruction) { /** * Update the state using a generic operation, - * based on contents of its output array, storing + * based on contents of its {@link RAVInstruction.Op#dests output array}, storing * symbols pre-allocation to current locations after * allocation. * - * @param op Operation we update state from + * @param op Operation we update state from */ protected void updateWithOp(RAVInstruction.Op op) { if (op.references != null) { @@ -518,8 +542,8 @@ protected void updateWithOp(RAVInstruction.Op op) { * Update block state with a safe point list of live references deemed by the GC, * any other references not included in said list are to be set as unknown so * there's no freed pointer use. - * - * References need to be retrieved using LocationMarker classes. + *

+ * References need to be retrieved using {@link jdk.graal.compiler.lir.dfa.LocationMarker} classes. * * @param op SafePoint we are using to remove old references */ @@ -566,7 +590,7 @@ protected void updateCalleeSavedRegisters() { return; } - for (var reg : registers) { + for (var reg : registers) { var regValue = RARegister.create(reg.asValue()); // Save same registers as symbol, and later check if it was retrieved @@ -578,6 +602,8 @@ protected void updateCalleeSavedRegisters() { * At exit point, check that all callee saved registers were * indeed correctly saved, by checking that the symbol stored * in said registers is equal to the registers themselves. + * + * @throws RAVException when callee saved register was not recovered */ protected void checkCalleeSavedRegisters() { var registers = this.registerAllocationConfig.getRegisterConfig().getCalleeSaveRegisters(); @@ -600,12 +626,12 @@ protected void checkCalleeSavedRegisters() { } /** - * Update state with a ValueMove, if locations is concrete, + * Update state with a {@link RAVInstruction.ValueMove}, if locations is concrete, * we set it to a variable/constant, if it's a variable to variable * move, then all locations containing old variable need to be changed * to the new variable. * - * @param valueMove Value move we update state from + * @param valueMove move we update state from */ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove) { if (valueMove.getLocation().isVariable()) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index 54a8801c43e6..fd867c86f3e3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -30,16 +30,17 @@ import java.util.List; /** - * Resolve ConflictedAllocationState occurrences based + * Resolve {@link ConflictedAllocationState} occurrences based * on internal set of rules. + * *

- * In-case comparison of ValueAllocationState fails, it + * In-case comparison of {@link ValueAllocationState} fails, it * also might get resolved by this. *

*/ public interface ConflictResolver { /** - * ConflictResolver can prepare its own internal state here + * {@link ConflictResolver} can prepare its own internal state here * so it can later resolve conflicts. * * @param lir LIR @@ -48,23 +49,23 @@ public interface ConflictResolver { void prepare(LIR lir, BlockMap> blockInstructions); /** - * Resolve an issue stemming from ValueAllocationState not having the correct value + * Resolve an issue stemming from {@link ValueAllocationState} not having the correct value * in verification phase. * * @param target Variable we are looking to resolve to * @param valueState Current ValueAllocationState instance * @param location Location where the valueState is stored - * @return ValueAllocationState instance if conflict is resolved, otherwise null. + * @return {@link ValueAllocationState} instance if conflict is resolved, otherwise null. */ ValueAllocationState resolveValueState(RAVariable target, ValueAllocationState valueState, RAValue location); /** - * Resolve a ConflictedAllocationState to ValueAllocationState based on the target variable. + * Resolve a {@link ConflictedAllocationState} to {@link ValueAllocationState} based on the target variable. * * @param target Variable we are looking to resolve to * @param conflictedState Set of conflicted states * @param location Location where the valueState is stored - * @return ValueAllocationState instance if conflict is resolved, otherwise null. + * @return {@link ValueAllocationState} instance if conflict is resolved, otherwise null. */ ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 1adbc8082de1..f89c9f213f67 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -31,10 +31,10 @@ import java.util.Set; /** - * Conflicted allocation state - two or more ValueAllocationState instances + * Conflicted allocation state - two or more instances * have collided and either of them can be stored at said location, needs - * to be resolved by either overwriting the location with a new ValueAllocationState instance - * or by a ConflictResolver implementation. + * to be resolved by either overwriting the location with a new {@link ValueAllocationState instance} + * or by a {@link ConflictResolver} implementation. */ public class ConflictedAllocationState extends AllocationState { protected Set conflictedStates; @@ -73,9 +73,9 @@ public boolean hasConflictedValue(ValueAllocationState valueAllocationState) { } /** - * Get the set of all ValueAllocationState instances conflicting in this state. + * Get the set of all {@link ValueAllocationState} instances conflicting in this state. * - * @return Set of ValueAllocationState instances + * @return Set of {@link ValueAllocationState} instances */ public Set getConflictedStates() { return this.conflictedStates; @@ -88,10 +88,10 @@ public boolean isConflicted() { /** * Any state coming here will be added to the conflict set - * and create a new ConflictedAllocationState instance. + * and create a new {@link ConflictedAllocationState} instance. * * @param other Other state coming from a predecessor edge - * @return ConflictedAllocationState with predecessor state added up + * @return {@link ConflictedAllocationState} with predecessor state added up */ @Override public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index 158188a877ed..f264ba3be627 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -38,7 +38,8 @@ import java.util.Set; /** - * Resolve conflicts made by the constant materialization process. + * Resolve {@link ConflictedAllocationState conflicts} made by the constant materialization process. + * *

* Also checks if the constant can materialize to stack. *

@@ -96,12 +97,12 @@ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock bloc /** * Resolve conflict of target variable and the constant it represents - * to the ValueAllocationState of the target variable. + * to the {@link ValueAllocationState} of the target variable. * * @param target Variable we are looking to resolve to - * @param conflictedState Set of conflicted states - * @param location Location where the valueState is stored - * @return target variable stored in ValueAllocationState or null. + * @param conflictedState Set of {@link ConflictedAllocationState conflicted states} + * @param location Location where the value is stored + * @return target variable stored in {@link ValueAllocationState} or null. */ @Override public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { @@ -147,12 +148,12 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted } /** - * Resolve ValueAllocationState of a constant to the target variable. + * Resolve {@link ValueAllocationState} of a constant to the target variable. * * @param variable Variable we are looking to resolve to - * @param valueState Current ValueAllocationState instance - * @param location Location where the valueState is stored - * @return target variable stored in ValueAllocationState or null. + * @param valueState Current {@link ValueAllocationState} instance + * @param location Location where the value is stored + * @return target variable stored in {@link ValueAllocationState} or null. */ @Override public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocationState valueState, RAValue location) { @@ -179,14 +180,14 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati /** * Check if variable can be rematerialized to said location based on the - * original instruction source, stored in ValueAllocationState. + * original instruction source, stored in {@link ValueAllocationState}. * *

* Check if variable cannot rematerialize to stack and if it did so. *

* * @param variable Target variable - * @param state State it is in + * @param state {@link AllocationState state} it is in * @return Was it rematerialized to wrong location? */ protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllocationState state) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 1e9a2c5d0e07..f5e432393200 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -381,8 +381,8 @@ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction * locations of variables based on the locations. * * If a variable is in location reg1 and a move - * is found reg1 = MOVE reg2, then said variable - * will now be in reg2, because reg1 will now + * is found reg1 = MOVE reg2, then said variable + * will now be in reg2, because reg1 will now * have different content when walking through * the instructions in reverse. * diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java index 7ce4bd28525f..c2e58342da8f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java @@ -28,7 +28,7 @@ /** * Invalid register was used in allocation - * as defined by the RegisterAllocationConfig. + * as defined by the {@link jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig}. */ @SuppressWarnings("serial") public class InvalidRegisterUsedException extends RAVException { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java index df0e4d1c0ece..1baed9236f17 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java @@ -31,7 +31,7 @@ import java.util.EnumSet; /** - * Value used in instruction does not satisfy it's operand flags, + * Value used in instruction does not satisfy it's {@link LIRInstruction.OperandFlag operand flags}, * for example if an operand is a stack slot, but should only * be a register. */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java index 38898d88686f..3e85bcf3b71d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java @@ -27,6 +27,10 @@ import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterValue; +/** + * Wrap around {@link RegisterValue} to only index + * by the name of the {@link Register} it holds. + */ public class RARegister extends RAValue { protected RegisterValue registerValue; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java index dcaeb2eeb57c..ce66375cbc1f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java @@ -29,7 +29,7 @@ /** * An internal error occurred within the * verification process, not caused by - * the Register Allocator. + * the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase register allocation}. */ @SuppressWarnings("serial") public class RAVError extends GraalError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java index 9fca8ada5fc6..c73af721a3fb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java @@ -28,8 +28,8 @@ /** * Register Allocation Verification Exception - - * a violation made by the Register Allocator occurred - * and will be thrown in verification phase. + * a violation made by the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase register allocation} + * occurred and will be thrown in verification phase. */ @SuppressWarnings("serial") public class RAVException extends GraalError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java index 9766a532c406..253d22a70845 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -27,8 +27,8 @@ import java.util.List; /** - * Composite exception taking every Register Allocation Verification - * exception that occurred (exceptions done by the Register Allocator) + * Composite exception taking every {@link RAVException exception} + * that occurred (exceptions caused by the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase}) * and combining them together to one exception. */ @SuppressWarnings("serial") diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index 3966e9827225..b6caa94fe5c4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -31,17 +31,18 @@ /** * Wrapper around Value to change how indexing - * in data structures like Map or Set is done. + * in data structures like {@link java.util.Map} or {@link java.util.Set} is done. + * *

- * Register is indexed by its name without the kind. - * Variable is indexed by its number index without the kind. - * Stack slots and constants remain as is, because the kind - * does not mess with indexing. + * Values are indexed without their {@link LIRKind kind} + * associated with them, this is necessary for {@link AllocationStateMap} + * because locations can change kinds and still be associated + * with one key/value pair in said map. *

*/ public class RAValue { /** - * Create a new RAValue instance from Value. + * Create a new RAValue instance from {@link Value}. * * @param value Value we are wrapping * @return Instance of RAValue @@ -107,8 +108,10 @@ public int hashCode() { } /** - * Equal RegisterValue on it's Register, not Register and kind, - * otherwise same as Value. + * Are two {@link RAValue values} equal? + * - check for offset for {@link jdk.vm.ci.code.StackSlot} + * - check for id for {@link jdk.graal.compiler.lir.VirtualStackSlot} + * - otherwise default to normal equals on {@link Value} * * @param other The reference object with which to compare. * @return Are said values equal? diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java index d671e10c9baa..6826f5c4c5c1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java @@ -27,10 +27,11 @@ import jdk.graal.compiler.lir.Variable; /** - * Wrapper around Variable to change how indexing - * in data structures like Map or Set is done. + * Wrapper around {@link Variable} to change how indexing + * in data structures like {@link java.util.Map} or {@link java.util.Set} is done. + * *

- * We index only by the Variable index instead of + * We index only by the {@link Variable} index instead of * including the kind as well. *

*/ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java index 1a0645d049fd..f7b404bf43a5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java @@ -50,10 +50,12 @@ * of it, to make sure GC-freed references are not * used further. * - * It uses the LocationMarker class that is used - * in the FinalCodeAnalysisStage where the actual + *

+ * It uses the {@link LocationMarker} class that is used + * in the {@link jdk.graal.compiler.lir.phases.FinalCodeAnalysisPhase} where the actual * set of references is built, we require this * information earlier. + *

*/ public class ReferencesBuilder { class ReferenceSet extends ValueSet { @@ -119,8 +121,10 @@ protected ReferenceSet newLiveValueSet() { /** * Only process values that are registers (that can contain references and are allocatable) * and stack slots, where kind cannot be Illegal. + * *

- * Take from LocationMarkerPhase.Marker.shouldProcessValue + * Take from {@link jdk.graal.compiler.lir.dfa.LocationMarkerPhase.Marker#shouldProcessValue} + *

* * @param operand Value to be processed * @return If matches criteria for LocationMarker processing @@ -165,7 +169,7 @@ protected void processState(LIRInstruction instruction, LIRFrameState info, Refe } /** - * Build references for RAV Op instructions to check GC roots. + * Build references for {@link RAVInstruction.Op intructions} to check GC roots. * * @param lir LIR * @param frameMap Frame map necessary to build references diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 873cb57da787..c902dda94e8a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -492,10 +492,10 @@ protected List handleSpeculativeMoves(RAVInstruction.O } /** - * Create Register Verifier Instruction that was created by the Register Allocator. + * Create Register Verifier Instruction that was created by the {@link RegisterAllocationPhase register allocator}. * Generally speaking, it's always a move instruction, other ones return null. * - * @param instruction LIR Instruction newly created by Register Allocator + * @param instruction LIR Instruction newly created by {@link RegisterAllocationPhase register allocator} * @return Spill, Reload, Move or null if instruction is not a move */ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { @@ -535,10 +535,11 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) } /** - * Determines if instruction is a virtual move, a virtual move is + * Determines if instruction is a virtual {@link RAVInstruction.ValueMove move}, a virtual move is * a move instruction that moves a real register value into a variable, * which is something that will always get removed from the final allocated * IR. + * *

* This information is important to the verification process and needs to * be part of the Verifier IR. @@ -558,14 +559,15 @@ protected boolean isVirtualMove(LIRInstruction instruction) { } /** - * Determines if a move is speculative - it could potentially be + * Determines if a {@link RAVInstruction.ValueMove move} is speculative - it could potentially be * removed, but hold important information to the verification process. + * *

* For example, this happens for a move between two variables and after * allocation locations are equal, making the move redundant. *

* - * @param instruction LIR instruction we are looking at + * @param instruction {@link LIRInstruction instruction} we are looking at * @return true, if instruction is a speculative move, otherwise false */ protected boolean isSpeculativeMove(LIRInstruction instruction) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java index 7977599345fa..6d719860bc27 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java @@ -25,13 +25,13 @@ package jdk.graal.compiler.lir.alloc.verifier; /** - * Re-materialized constant has wrong source (not a Value Move), + * Re-materialized constant has wrong source (not a {@link RAVInstruction.ValueMove}), * but either undefined or something different. */ @SuppressWarnings("serial") public class RematerializedConstantSourceMissingError extends RAVError { public RematerializedConstantSourceMissingError(RAVInstruction.Base instruction, RAVariable variable) { - super(variable + " was materialized to " + instruction); + super(getErrorMessage(instruction, variable)); } public static String getErrorMessage(RAVInstruction.Base instruction, RAVariable variable) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index 120e5ef7ec71..57b056abb4bc 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -32,7 +32,7 @@ */ public class UnknownAllocationState extends AllocationState { /** - * Single instance used for all occurrences of Unknown state. + * Single instance used for all occurrences of {@link UnknownAllocationState unknown state}. */ public static UnknownAllocationState INSTANCE = new UnknownAllocationState(); @@ -43,10 +43,10 @@ public boolean isUnknown() { /** * Meet state from predecessor, if both are unknown then unknown is returned, - * otherwise conflict occurs. + * otherwise {@link ConflictedAllocationState conflict} occurs. * * @param other Other state coming from a predecessor edge - * @return Unknown if both are, otherwise a conflict + * @return {@link UnknownAllocationState Unknown} if both are, otherwise a conflict */ @Override public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java index 2cb7867b78d7..076cfa96f67a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java @@ -31,7 +31,7 @@ /** * Unknown instruction was found after the allocation, * this usually means that it's a different instruction - * from a Move, and we do not know how to handle it. + * from a move, and we do not know how to handle it. */ @SuppressWarnings("serial") public class UnknownInstructionError extends RAVError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 91a5079feafe..75db866337d6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -31,8 +31,8 @@ import jdk.vm.ci.meta.Value; /** - * Allocation state holding a single, concrete Value (wrapped with RAValue), - * also accompanied by instruction and block where it was created. + * Allocation state holding a single, concrete {@link Value} (wrapped with {@link RAValue}), + * also accompanied by {@link RAVInstruction instruction} and {@link BasicBlock block} where it was created. */ public class ValueAllocationState extends AllocationState implements Cloneable { protected RAValue value; @@ -66,10 +66,10 @@ public ValueAllocationState(ValueAllocationState other) { /** * Create an illegal value allocation state, used - * as a substitute for Unknown state when creating - * a conflict. + * as a substitute for {@link UnknownAllocationState unknown} when creating + * a {@link ConflictedAllocationState conflict}. * - * @return instance of ValueAllocationState holding Value.ILLEGAL. + * @return instance of {@link ValueAllocationState} holding {@link Value#ILLEGAL}. */ public static ValueAllocationState createIllegal(BasicBlock block) { return new ValueAllocationState(new RAValue(Value.ILLEGAL), null, block); @@ -92,12 +92,12 @@ public BasicBlock getBlock() { } /** - * Meet a state from predecessor block, if it's ValueAllocationState + * Meet a state from predecessor block, if it's {@link ValueAllocationState} * and contents are equal, then same state is returned, otherwise - * a conflict is created between said states. + * a {@link ConflictedAllocationState conflict} is created between said states. * * @param other Other state coming from a predecessor edge - * @return ValueAllocationState if their contents are equal, otherwise ConflictedAllocationState. + * @return {@link ValueAllocationState} if their contents are equal, otherwise {@link ConflictedAllocationState}. */ public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { if (other.isUnknown()) { From 8f88fa29e05d86655a810778c4bfbe91eb9d5958 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 18 Mar 2026 11:57:48 +0100 Subject: [PATCH 076/112] Fix warnings from ci --- .../lir/alloc/verifier/AllocationState.java | 1 + .../alloc/verifier/BlockVerifierState.java | 22 +++++++++---------- .../OperandFlagMismatchException.java | 2 +- .../lir/alloc/verifier/RAVInstruction.java | 16 +++++++++----- .../lir/alloc/verifier/ReferencesBuilder.java | 2 +- .../alloc/verifier/RegAllocVerifierPhase.java | 5 +++-- .../alloc/verifier/ValueAllocationState.java | 11 ++++++---- 7 files changed, 34 insertions(+), 25 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index 8057c0e042ad..e4e97db51d8c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -65,6 +65,7 @@ public boolean isConflicted() { * * @return Newly copied state */ + @Override public abstract AllocationState clone(); /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index a5feaca4df1b..133729e6e877 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -116,13 +116,13 @@ protected void checkStateValues(RAVInstruction.Op op) { * Verify the correspondence of original variables used in instructions * are stored in the state of current locations. * - * @param values Array of pairs of current location and original variable. + * @param valuePairs Array of pairs of current location and original variable. * @param op Operation this input array of values belongs to */ - protected void checkInputs(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op) { + protected void checkInputs(RAVInstruction.ValueArrayPair valuePairs, RAVInstruction.Op op) { // Check that incoming values are not unknown or conflicted - these only matter if used - for (int idx = 0; idx < values.count; idx++) { - checkOperand(values.orig[idx], values.curr[idx], op); + for (int idx = 0; idx < valuePairs.count; idx++) { + checkOperand(valuePairs.orig[idx], valuePairs.curr[idx], op); } } @@ -305,7 +305,7 @@ public void check(RAVInstruction.Base instruction) { /** * Check if the destination of a move has the correct type to - * store the {@link ValueAllocationState value} + * store the {@link ValueAllocationState value}. * * @param move Move between locations, inserted by register allocator */ @@ -422,22 +422,22 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction) { /** * Make sure concrete current locations changed by the allocator - * are not violating set of {@link LIRInstruction.OperandFlag flags}, + * are not violating set of {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag flags}, * which specify what type can they be. This is done on every * array of pairs (dest, uses, alive, temp). * - * @param values Value array pair we are verifying + * @param valuePairs Value array pair we are verifying * @param op Instruction which holds this array, for tracing in exceptions * @throws OperandFlagMismatchException Operand is a wrong type based on OperandFlag set. */ - protected void checkOperandFlags(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op) { - for (int i = 0; i < values.count; i++) { - var curr = values.curr[i]; + protected void checkOperandFlags(RAVInstruction.ValueArrayPair valuePairs, RAVInstruction.Op op) { + for (int i = 0; i < valuePairs.count; i++) { + var curr = valuePairs.curr[i]; if (curr == null) { continue; } - var flags = values.operandFlags.get(i); + var flags = valuePairs.operandFlags.get(i); var currValue = curr.getValue(); if (LIRValueUtil.isStackSlotValue(currValue)) { if (flags.contains(LIRInstruction.OperandFlag.STACK)) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java index 1baed9236f17..f783bd1a8c1e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java @@ -31,7 +31,7 @@ import java.util.EnumSet; /** - * Value used in instruction does not satisfy it's {@link LIRInstruction.OperandFlag operand flags}, + * Value used in instruction does not satisfy it's {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag operand flags}, * for example if an operand is a stack slot, but should only * be a register. */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 0ad4b7fa0769..9061c0234bed 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -28,14 +28,12 @@ import jdk.graal.compiler.hotspot.amd64.AMD64HotSpotSafepointOp; import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; -import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.graal.compiler.lir.aarch64.AArch64Call; import jdk.graal.compiler.lir.amd64.AMD64Call; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaValue; import jdk.vm.ci.meta.Value; @@ -283,18 +281,18 @@ public static class Op extends Base { * Count number of values stored. */ private final class GetCountProcedure implements InstructionValueProcedure { - private int count = 0; + private int valueCount = 0; public int getCount() { - int count = this.count; + int count = this.valueCount; // Reset the count and go again for different argument type - this.count = 0; + this.valueCount = 0; return count; } @Override public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - count++; + valueCount++; return value; } } @@ -392,6 +390,7 @@ public LocationMove(LIRInstruction instr, Value from, Value to) { this.to = RAValue.create(to); } + @Override public String toString() { return to.toString() + " = MOVE " + from.toString(); } @@ -410,6 +409,7 @@ public RegMove(LIRInstruction instr, RegisterValue from, RegisterValue to) { this.to = RAValue.create(to); } + @Override public String toString() { return to.toString() + " = REGMOVE " + from.toString(); } @@ -439,6 +439,7 @@ public StackMove(LIRInstruction instr, Value from, Value to, Value backupSlot) { this.backupSlot = RAValue.create(backupSlot); } + @Override public String toString() { return to.toString() + " = STACKMOVE " + from.toString(); } @@ -457,6 +458,7 @@ public Reload(LIRInstruction instr, RegisterValue to, Value from) { this.to = RAValue.create(to); } + @Override public String toString() { return to.toString() + " = RELOAD " + from.toString(); } @@ -475,6 +477,7 @@ public Spill(LIRInstruction instr, Value to, RegisterValue from) { this.to = RAValue.create(to); } + @Override public String toString() { return to.toString() + " = SPILL " + from.toString(); } @@ -498,6 +501,7 @@ public ValueMove(LIRInstruction instr, Value variableOrConstant, Value location) this.location = RAValue.create(location); } + @Override public String toString() { return getLocation().toString() + " = VIRTMOVE " + variableOrConstant.toString(); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java index f7b404bf43a5..694a41cb366f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java @@ -123,7 +123,7 @@ protected ReferenceSet newLiveValueSet() { * and stack slots, where kind cannot be Illegal. * *

- * Take from {@link jdk.graal.compiler.lir.dfa.LocationMarkerPhase.Marker#shouldProcessValue} + * Take from jdk.graal.compiler.lir.dfa.LocationMarkerPhase.Marker#shouldProcessValue *

* * @param operand Value to be processed diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index c902dda94e8a..c233c7095834 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -64,7 +64,7 @@ * the actual allocator and validates that order of spills, reloads * and moves is correct and that variables before allocation * are actually stored in current locations chosen by the allocator. - * + *

* Needs to extend RegisterAllocationPhase to not throw an exception. */ public class RegAllocVerifierPhase extends RegisterAllocationPhase { @@ -309,9 +309,10 @@ protected RegisterAllocationConfig getRegisterAllocationConfig(AllocationContext * * @param lir LIR * @param preallocMap Pre-allocation map to keep track of virtual values + * @param _ctx Context of the allocation, kept here so tests can access this value here * @return Verifier IR */ - protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext context) { + protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext _ctx) { Map definedVariables = new EconomicHashMap<>(); var presentInstructions = preprocessAllocatedInstructions(lir, preallocMap, definedVariables); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 75db866337d6..b1455ece2b2d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -40,8 +40,8 @@ public class ValueAllocationState extends AllocationState implements Cloneable { protected BasicBlock block; public ValueAllocationState(RAValue raValue, RAVInstruction.Base source, BasicBlock block) { - var value = raValue.getValue(); - if (ValueUtil.isRegister(value) || LIRValueUtil.isVariable(value) || LIRValueUtil.isConstantValue(value) || LIRValueUtil.isStackSlotValue(value) || Value.ILLEGAL.equals(value)) { + var v = raValue.getValue(); + if (ValueUtil.isRegister(v) || LIRValueUtil.isVariable(v) || LIRValueUtil.isConstantValue(v) || LIRValueUtil.isStackSlotValue(v) || Value.ILLEGAL.equals(v)) { // Here, we make sure that no new value class is used here, without consideration. // StackSlot, RegisterValue is present in start block in label as predefined argument @@ -54,7 +54,7 @@ public ValueAllocationState(RAValue raValue, RAVInstruction.Base source, BasicBl this.source = source; this.block = block; } else { - throw GraalError.shouldNotReachHere("Invalid type of value used " + value); + throw GraalError.shouldNotReachHere("Invalid type of value used " + v); } } @@ -97,9 +97,12 @@ public BasicBlock getBlock() { * a {@link ConflictedAllocationState conflict} is created between said states. * * @param other Other state coming from a predecessor edge + * @param otherBlock Where the other state is coming from + * @param currBlock Where the current state is coming from * @return {@link ValueAllocationState} if their contents are equal, otherwise {@link ConflictedAllocationState}. */ - public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { + @Override + public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock currBlock) { if (other.isUnknown()) { // Unknown is coming from different predecessor where this location // is undefined, meaning this value is not always accessible in the successor From b9febcf011f5035067c5b5585951aa80b00b9365 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 18 Mar 2026 12:37:20 +0100 Subject: [PATCH 077/112] Run eclipse formatter --- .../AliveConstraintViolationException.java | 11 +- .../lir/alloc/verifier/AllocationState.java | 19 ++- .../alloc/verifier/AllocationStateMap.java | 45 +++---- .../alloc/verifier/BlockVerifierState.java | 124 ++++++++---------- .../lir/alloc/verifier/ConflictResolver.java | 27 ++-- .../verifier/ConflictedAllocationState.java | 18 ++- ...nstantMaterializationConflictResolver.java | 22 ++-- .../verifier/FromUsageResolverGlobal.java | 71 ++++------ .../InvalidRegisterUsedException.java | 4 +- .../verifier/KindsMismatchException.java | 8 +- .../alloc/verifier/MissingLocationError.java | 4 +- .../OperandFlagMismatchException.java | 16 +-- .../lir/alloc/verifier/RARegister.java | 6 +- .../compiler/lir/alloc/verifier/RAVError.java | 5 +- .../lir/alloc/verifier/RAVException.java | 6 +- .../RAVFailedVerificationException.java | 6 +- .../lir/alloc/verifier/RAVInstruction.java | 59 ++++----- .../compiler/lir/alloc/verifier/RAValue.java | 18 ++- .../lir/alloc/verifier/RAVariable.java | 7 +- .../lir/alloc/verifier/ReferencesBuilder.java | 16 +-- .../lir/alloc/verifier/RegAllocVerifier.java | 58 ++++---- .../alloc/verifier/RegAllocVerifierPhase.java | 118 ++++++++--------- ...aterializedConstantSourceMissingError.java | 4 +- .../verifier/UnknownAllocationState.java | 7 +- .../verifier/UnknownInstructionError.java | 5 +- .../alloc/verifier/ValueAllocationState.java | 23 ++-- .../verifier/ValueNotInRegisterException.java | 8 +- .../lir/alloc/verifier/VerifierPrinter.java | 7 +- 28 files changed, 334 insertions(+), 388 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index 3ad79b64be0b..d36b8ed5719e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -28,9 +28,8 @@ import jdk.graal.compiler.lir.LIRInstruction; /** - * Violation of the alive inputs occurred, - * same location was marked as alive argument - * as well as temp or output. + * Violation of the alive inputs occurred, same location was marked as alive argument as well as + * temp or output. */ @SuppressWarnings("serial") public class AliveConstraintViolationException extends RAVException { @@ -41,9 +40,9 @@ public class AliveConstraintViolationException extends RAVException { * Construct an AliveConstraintViolationException. * * @param instruction Instruction where violation occurred - * @param block Block where violation occurred - * @param location Location that is being shared - * @param asDest Alive location was used as an output + * @param block Block where violation occurred + * @param location Location that is being shared + * @param asDest Alive location was used as an output */ public AliveConstraintViolationException(LIRInstruction instruction, BasicBlock block, RAValue location, boolean asDest) { super(AliveConstraintViolationException.getErrorMessage(instruction, block, location, asDest)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index e4e97db51d8c..61fb89656278 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -27,13 +27,12 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; /** - * Interface for state concrete location is in, - * stored in {@link AllocationStateMap}. + * Interface for state concrete location is in, stored in {@link AllocationStateMap}. */ public abstract class AllocationState { /** - * Get the default allocation state for every location, - * instead of null, we have {@link UnknownAllocationState unknown} state. + * Get the default allocation state for every location, instead of null, we have + * {@link UnknownAllocationState unknown} state. * * @return Default state for every location */ @@ -60,8 +59,7 @@ public boolean isConflicted() { } /** - * Create a copy of this state, necessary - * for state copies made over program graph edges. + * Create a copy of this state, necessary for state copies made over program graph edges. * * @return Newly copied state */ @@ -69,13 +67,12 @@ public boolean isConflicted() { public abstract AllocationState clone(); /** - * Meet a state from different block coming from edge in - * the program graph, decide what result of said two states - * should be. + * Meet a state from different block coming from edge in the program graph, decide what result + * of said two states should be. * - * @param other Other state coming from a predecessor edge + * @param other Other state coming from a predecessor edge * @param otherBlock Which block is other state from - * @param block Which state is this state from + * @param block Which state is this state from * @return What is the new state the location is in. */ public abstract AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java index 8a6eb3ac0fbd..128e611c4b9a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java @@ -33,16 +33,16 @@ import java.util.Set; /** - * Mapping between a location and allocation state, - * that stores one of these: - * - {@link UnknownAllocationState unknown} - our null state, nothing was stored yet - * - {@link ValueAllocationState value} - symbol that is stored at said location - * - {@link ConflictedAllocationState conflicted} - set of Values that are supposed to be at same location + * Mapping between a location and allocation state, that stores one of these: - + * {@link UnknownAllocationState unknown} - our null state, nothing was stored yet - + * {@link ValueAllocationState value} - symbol that is stored at said location - + * {@link ConflictedAllocationState conflicted} - set of Values that are supposed to be at same + * location * * Conflicts are resolved by assigning new {@link ValueAllocationState value} to same location. - * Otherwise, they cannot be used. {@link ValueAllocationState Value} can store register, stack slot, - * constant, but most importantly variables used before allocation. These - * are what we are checking with the verification process. + * Otherwise, they cannot be used. {@link ValueAllocationState Value} can store register, stack + * slot, constant, but most importantly variables used before allocation. These are what we are + * checking with the verification process. */ public class AllocationStateMap { protected BasicBlock block; @@ -53,8 +53,7 @@ public class AllocationStateMap { protected Map internalMap; /** - * Register allocation config describing which registers - * can be used. + * Register allocation config describing which registers can be used. */ protected RegisterAllocationConfig registerAllocationConfig; @@ -87,10 +86,9 @@ public AllocationState get(RAValue key, AllocationState defaultValue) { } /** - * Put a new state for location to the map, - * while checking if register can be allocated to. + * Put a new state for location to the map, while checking if register can be allocated to. * - * @param key Location used + * @param key Location used * @param state State to store */ public void put(RAValue key, AllocationState state) { @@ -99,16 +97,15 @@ public void put(RAValue key, AllocationState state) { } /** - * Put a new state for location to the map, - * without checking if the register can actually be used. + * Put a new state for location to the map, without checking if the register can actually be + * used. * *

- * This is useful for registers that are used by the ABI - * in the first label, but can actually never be changed, - * like rbp. + * This is useful for registers that are used by the ABI in the first label, but can actually + * never be changed, like rbp. *

* - * @param key Location used + * @param key Location used * @param state State to store */ public void putWithoutRegCheck(RAValue key, AllocationState state) { @@ -118,7 +115,7 @@ public void putWithoutRegCheck(RAValue key, AllocationState state) { /** * Put a copied state to a location, used when merging. * - * @param key Location used + * @param key Location used * @param state State to store */ public void putClone(RAValue key, AllocationState state) { @@ -149,8 +146,8 @@ public Set getValueLocations(RAValue value) { } /** - * Merge two maps together, source is generally - * the predecessor to the current block (this state map). + * Merge two maps together, source is generally the predecessor to the current block (this state + * map). * * @param source Predecessor merging to here * @return Was this map changed? @@ -177,8 +174,8 @@ public boolean mergeWith(AllocationStateMap source) { } /** - * Check if register can be used by the register allocator. - * If not allowed, an exception is thrown. + * Check if register can be used by the register allocator. If not allowed, an exception is + * thrown. * * @param location Value that could be a register. */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 133729e6e877..7ffce23124a7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -38,10 +38,9 @@ import jdk.vm.ci.meta.ValueKind; /** - * Verification state a block is in, holds a mapping - * between locations and their allocation states, which - * can be unknown, value - it's contents or - * conflicted - multiple values conflict with each other. + * Verification state a block is in, holds a mapping between locations and their allocation states, + * which can be unknown, value - it's contents or conflicted - multiple values conflict with each + * other. */ public class BlockVerifierState { /** @@ -50,8 +49,7 @@ public class BlockVerifierState { public AllocationStateMap values; /** - * Register allocation config we use to check if only - * allocatable registers are the ones used. + * Register allocation config we use to check if only allocatable registers are the ones used. */ protected RegisterAllocationConfig registerAllocationConfig; @@ -84,9 +82,8 @@ public AllocationStateMap getValues() { } /** - * Merge states of block and it's predecessor. This process - * creates a new state based on contents of the predecessor, - * creating conflicts where current locations do not match. + * Merge states of block and it's predecessor. This process creates a new state based on + * contents of the predecessor, creating conflicts where current locations do not match. * * @param other Predecessor of this block * @return Was this state changed? @@ -96,9 +93,9 @@ public boolean meetWith(BlockVerifierState other) { } /** - * Check state values stored in LIRFrameState same way other operands, - * except we skip those where the state values are incorrectly stored, - * after stack allocation, because some values are no longer present. + * Check state values stored in LIRFrameState same way other operands, except we skip those + * where the state values are incorrectly stored, after stack allocation, because some values + * are no longer present. * * @param op Operation for which we are checking state values */ @@ -113,11 +110,11 @@ protected void checkStateValues(RAVInstruction.Op op) { } /** - * Verify the correspondence of original variables used in instructions - * are stored in the state of current locations. + * Verify the correspondence of original variables used in instructions are stored in the state + * of current locations. * * @param valuePairs Array of pairs of current location and original variable. - * @param op Operation this input array of values belongs to + * @param op Operation this input array of values belongs to */ protected void checkInputs(RAVInstruction.ValueArrayPair valuePairs, RAVInstruction.Op op) { // Check that incoming values are not unknown or conflicted - these only matter if used @@ -127,18 +124,17 @@ protected void checkInputs(RAVInstruction.ValueArrayPair valuePairs, RAVInstruct } /** - * Check that original variable matches symbol stored at - * the current location in the {@link AllocationStateMap}. + * Check that original variable matches symbol stored at the current location in the + * {@link AllocationStateMap}. * *

- * We also check that kinds match and possibly - * rematerialize variables at this point in the + * We also check that kinds match and possibly rematerialize variables at this point in the * state map. *

* * @param orig Original variable * @param curr Current location - * @param op Operation where these are used + * @param op Operation where these are used */ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { assert orig != null; @@ -162,7 +158,8 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { // Skip when jump due to this case: // "rdx|QWORD[*] = MOVE input: rdx|QWORD[.+] moveKind: QWORD" // this move is inserted by the allocator and changes type - // of rdx from [.+] (compressed reference) (same as original variable) to [*] (invalid reference) + // of rdx from [.+] (compressed reference) (same as original variable) + // to [*] (invalid reference) } AllocationState state = this.values.get(curr); @@ -251,11 +248,10 @@ protected boolean kindsEqual(RAValue orig, RAValue curr) { * Are kinds equal even when {@link jdk.graal.compiler.lir.CastValue cast value} is present? * *

- * We need to ignore the cast value because the currently stored - * value will not be cast. + * We need to ignore the cast value because the currently stored value will not be cast. *

* - * @param orig Original variable + * @param orig Original variable * @param fromState Value stored in state of the current location * @return Are they equal? */ @@ -270,8 +266,8 @@ protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { } /** - * Check that all instruction arrays of pairs of original variable and current location - * check out to the state stored in for this block. + * Check that all instruction arrays of pairs of original variable and current location check + * out to the state stored in for this block. * * @param instruction Instruction we are checking */ @@ -304,8 +300,8 @@ public void check(RAVInstruction.Base instruction) { } /** - * Check if the destination of a move has the correct type to - * store the {@link ValueAllocationState value}. + * Check if the destination of a move has the correct type to store the + * {@link ValueAllocationState value}. * * @param move Move between locations, inserted by register allocator */ @@ -320,11 +316,10 @@ protected void checkMoveKinds(RAVInstruction.LocationMove move) { } /** - * Check {@link jdk.vm.ci.code.BytecodeFrame frames}, before and - * after allocation, mainly checking that {@link LIRKind} is a - * reference when {@link JavaKind} is an Object and wise-versa, - * checking that {@link LIRKind} is not a reference when - * {@link JavaKind} is not an object. + * Check {@link jdk.vm.ci.code.BytecodeFrame frames}, before and after allocation, mainly + * checking that {@link LIRKind} is a reference when {@link JavaKind} is an Object and + * wise-versa, checking that {@link LIRKind} is not a reference when {@link JavaKind} is not an + * object. * * @param op Operation holding said frames * @throws RAVException when a violation occurs @@ -368,8 +363,8 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { } /** - * Check if kinds in the {@link RAVInstruction.Op#temp temporary array} - * match before allocation original variables with after allocation concrete locations. + * Check if kinds in the {@link RAVInstruction.Op#temp temporary array} match before allocation + * original variables with after allocation concrete locations. * * @param op Instruction we update state from * @throws KindsMismatchException if a pair does not match @@ -386,11 +381,9 @@ protected void checkTempKind(RAVInstruction.Op op) { } } - /** - * Check if alive constraint is not being violated, - * when one location is supposed to be alive after instruction - * is complete, but is used either as an output or a generic input. + * Check if alive constraint is not being violated, when one location is supposed to be alive + * after instruction is complete, but is used either as an output or a generic input. * * @param instruction Instruction with alive inputs * @throws AliveConstraintViolationException throw when violation occurs @@ -421,13 +414,12 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction) { } /** - * Make sure concrete current locations changed by the allocator - * are not violating set of {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag flags}, - * which specify what type can they be. This is done on every - * array of pairs (dest, uses, alive, temp). + * Make sure concrete current locations changed by the allocator are not violating set of + * {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag flags}, which specify what type can + * they be. This is done on every array of pairs (dest, uses, alive, temp). * * @param valuePairs Value array pair we are verifying - * @param op Instruction which holds this array, for tracing in exceptions + * @param op Instruction which holds this array, for tracing in exceptions * @throws OperandFlagMismatchException Operand is a wrong type based on OperandFlag set. */ protected void checkOperandFlags(RAVInstruction.ValueArrayPair valuePairs, RAVInstruction.Op op) { @@ -462,9 +454,9 @@ protected void checkOperandFlags(RAVInstruction.ValueArrayPair valuePairs, RAVIn } /** - * Update the current state based on outputs of this instruction. - * Setting contents of current location in {@link AllocationStateMap} to - * the symbol that was present before allocation was completed. + * Update the current state based on outputs of this instruction. Setting contents of current + * location in {@link AllocationStateMap} to the symbol that was present before allocation was + * completed. * * @param instruction Instruction we update state from */ @@ -485,10 +477,9 @@ public void update(RAVInstruction.Base instruction) { } /** - * Update the state using a generic operation, - * based on contents of its {@link RAVInstruction.Op#dests output array}, storing - * symbols pre-allocation to current locations after - * allocation. + * Update the state using a generic operation, based on contents of its + * {@link RAVInstruction.Op#dests output array}, storing symbols pre-allocation to current + * locations after allocation. * * @param op Operation we update state from */ @@ -517,7 +508,8 @@ protected void updateWithOp(RAVInstruction.Op op) { if (location.equals(variable)) { // Only check register validity if it was changed by the register allocator - // for example: rbp is used as input to start block and forbidden to be used by the allocator + // for example: rbp is used as input to start block and forbidden to be used by the + // allocator this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op, block)); } else { this.values.put(location, new ValueAllocationState(variable, op, block)); @@ -539,11 +531,14 @@ protected void updateWithOp(RAVInstruction.Op op) { } /** - * Update block state with a safe point list of live references deemed by the GC, - * any other references not included in said list are to be set as unknown so - * there's no freed pointer use. + * Update block state with a safe point list of live references deemed by the GC, any other + * references not included in said list are to be set as unknown so there's no freed pointer + * use. + * *

- * References need to be retrieved using {@link jdk.graal.compiler.lir.dfa.LocationMarker} classes. + * References need to be retrieved using {@link jdk.graal.compiler.lir.dfa.LocationMarker} + * classes. + *

* * @param op SafePoint we are using to remove old references */ @@ -580,9 +575,8 @@ protected void updateWithSafePoint(RAVInstruction.Op op) { } /** - * Take list of callee saved registers and add them to the start - * block state with their own values as symbols in order to check - * that they were correctly retrieved at exit point. + * Take list of callee saved registers and add them to the start block state with their own + * values as symbols in order to check that they were correctly retrieved at exit point. */ protected void updateCalleeSavedRegisters() { var registers = this.registerAllocationConfig.getRegisterConfig().getCalleeSaveRegisters(); @@ -599,9 +593,8 @@ protected void updateCalleeSavedRegisters() { } /** - * At exit point, check that all callee saved registers were - * indeed correctly saved, by checking that the symbol stored - * in said registers is equal to the registers themselves. + * At exit point, check that all callee saved registers were indeed correctly saved, by checking + * that the symbol stored in said registers is equal to the registers themselves. * * @throws RAVException when callee saved register was not recovered */ @@ -626,10 +619,9 @@ protected void checkCalleeSavedRegisters() { } /** - * Update state with a {@link RAVInstruction.ValueMove}, if locations is concrete, - * we set it to a variable/constant, if it's a variable to variable - * move, then all locations containing old variable need to be changed - * to the new variable. + * Update state with a {@link RAVInstruction.ValueMove}, if locations is concrete, we set it to + * a variable/constant, if it's a variable to variable move, then all locations containing old + * variable need to be changed to the new variable. * * @param valueMove move we update state from */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index fd867c86f3e3..7214999ba201 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -30,41 +30,40 @@ import java.util.List; /** - * Resolve {@link ConflictedAllocationState} occurrences based - * on internal set of rules. + * Resolve {@link ConflictedAllocationState} occurrences based on internal set of rules. * *

- * In-case comparison of {@link ValueAllocationState} fails, it - * also might get resolved by this. + * In-case comparison of {@link ValueAllocationState} fails, it also might get resolved by this. *

*/ public interface ConflictResolver { /** - * {@link ConflictResolver} can prepare its own internal state here - * so it can later resolve conflicts. + * {@link ConflictResolver} can prepare its own internal state here so it can later resolve + * conflicts. * - * @param lir LIR + * @param lir LIR * @param blockInstructions IR of the Verifier */ void prepare(LIR lir, BlockMap> blockInstructions); /** - * Resolve an issue stemming from {@link ValueAllocationState} not having the correct value - * in verification phase. + * Resolve an issue stemming from {@link ValueAllocationState} not having the correct value in + * verification phase. * - * @param target Variable we are looking to resolve to + * @param target Variable we are looking to resolve to * @param valueState Current ValueAllocationState instance - * @param location Location where the valueState is stored + * @param location Location where the valueState is stored * @return {@link ValueAllocationState} instance if conflict is resolved, otherwise null. */ ValueAllocationState resolveValueState(RAVariable target, ValueAllocationState valueState, RAValue location); /** - * Resolve a {@link ConflictedAllocationState} to {@link ValueAllocationState} based on the target variable. + * Resolve a {@link ConflictedAllocationState} to {@link ValueAllocationState} based on the + * target variable. * - * @param target Variable we are looking to resolve to + * @param target Variable we are looking to resolve to * @param conflictedState Set of conflicted states - * @param location Location where the valueState is stored + * @param location Location where the valueState is stored * @return {@link ValueAllocationState} instance if conflict is resolved, otherwise null. */ ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index f89c9f213f67..6e2a14352506 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -31,10 +31,9 @@ import java.util.Set; /** - * Conflicted allocation state - two or more instances - * have collided and either of them can be stored at said location, needs - * to be resolved by either overwriting the location with a new {@link ValueAllocationState instance} - * or by a {@link ConflictResolver} implementation. + * Conflicted allocation state - two or more instances have collided and either of them can be + * stored at said location, needs to be resolved by either overwriting the location with a new + * {@link ValueAllocationState instance} or by a {@link ConflictResolver} implementation. */ public class ConflictedAllocationState extends AllocationState { protected Set conflictedStates; @@ -87,8 +86,8 @@ public boolean isConflicted() { } /** - * Any state coming here will be added to the conflict set - * and create a new {@link ConflictedAllocationState} instance. + * Any state coming here will be added to the conflict set and create a new + * {@link ConflictedAllocationState} instance. * * @param other Other state coming from a predecessor edge * @return {@link ConflictedAllocationState} with predecessor state added up @@ -124,10 +123,9 @@ public AllocationState clone() { } /** - * We do not compare conflicted state on its contents, - * whenever new one would be created as a result, the - * set of contents would remain the same, if values - * are equal based on RAValue rules. + * We do not compare conflicted state on its contents, whenever new one would be created as a + * result, the set of contents would remain the same, if values are equal based on RAValue + * rules. * * @param other Other state we are comparing it to * @return Are both states conflicted? diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index f264ba3be627..f51466293f13 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -56,7 +56,7 @@ public ConstantMaterializationConflictResolver() { /** * Creates a variable to constant mapping. * - * @param lir LIR + * @param lir LIR * @param blockInstructions IR of the Verifier */ @Override @@ -75,7 +75,7 @@ public void prepare(LIR lir, BlockMap> blockInstructio * Add variable to constant mapping from instruction contents. * * @param instruction Instruction we are looking at for LoadConstantOp - * @param block Block where instruction is from + * @param block Block where instruction is from */ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { @@ -96,12 +96,12 @@ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock bloc } /** - * Resolve conflict of target variable and the constant it represents - * to the {@link ValueAllocationState} of the target variable. + * Resolve conflict of target variable and the constant it represents to the + * {@link ValueAllocationState} of the target variable. * - * @param target Variable we are looking to resolve to + * @param target Variable we are looking to resolve to * @param conflictedState Set of {@link ConflictedAllocationState conflicted states} - * @param location Location where the value is stored + * @param location Location where the value is stored * @return target variable stored in {@link ValueAllocationState} or null. */ @Override @@ -150,9 +150,9 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted /** * Resolve {@link ValueAllocationState} of a constant to the target variable. * - * @param variable Variable we are looking to resolve to + * @param variable Variable we are looking to resolve to * @param valueState Current {@link ValueAllocationState} instance - * @param location Location where the value is stored + * @param location Location where the value is stored * @return target variable stored in {@link ValueAllocationState} or null. */ @Override @@ -179,15 +179,15 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati } /** - * Check if variable can be rematerialized to said location based on the - * original instruction source, stored in {@link ValueAllocationState}. + * Check if variable can be rematerialized to said location based on the original instruction + * source, stored in {@link ValueAllocationState}. * *

* Check if variable cannot rematerialize to stack and if it did so. *

* * @param variable Target variable - * @param state {@link AllocationState state} it is in + * @param state {@link AllocationState state} it is in * @return Was it rematerialized to wrong location? */ protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllocationState state) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index f5e432393200..a48a68b59a5c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -40,25 +40,21 @@ import java.util.Set; /** - * Resolve variables phi variables back to labels - * and jumps by find their first usage and handling - * any reg allocator inserted moves back to the defining - * label. + * Resolve variables phi variables back to labels and jumps by find their first usage and handling + * any reg allocator inserted moves back to the defining label. * - * Register allocator strips us of this information that is - * necessary for the verification. In order to avoid modifying - * the existing allocators, we rather try to resolve this - * information from other instructions. + * Register allocator strips us of this information that is necessary for the verification. In order + * to avoid modifying the existing allocators, we rather try to resolve this information from other + * instructions. * - * Variables with only usage in jump instructions are - * marked as aliases and are resolved after their successors. + * Variables with only usage in jump instructions are marked as aliases and are resolved after their + * successors. * - * If a variable has no usage, then no location is resolved - * and verification continues without issues. + * If a variable has no usage, then no location is resolved and verification continues without + * issues. * - * In the case the first usage of a label-defined variable - * is wrong, then JUMP instructions from predecessors - * fail the verification - wrong register will be chosen. + * In the case the first usage of a label-defined variable is wrong, then JUMP instructions from + * predecessors fail the verification - wrong register will be chosen. */ public class FromUsageResolverGlobal { /** @@ -87,21 +83,18 @@ public class FromUsageResolverGlobal { public Map reached; /** - * Mapping of operation to a set of variable for which - * this operation is a first usage. + * Mapping of operation to a set of variable for which this operation is a first usage. */ public Map> firstUsages; /** - * Initial locations of label-defined variables - * to set them to when their first usage is found. + * Initial locations of label-defined variables to set them to when their first usage is found. */ public Map initialLocations; /** - * Variable and a block where it's coming from (last jump instruction), - * this variable is aliased by the succeeding label, which needs to - * be resolved first, before this one can be. + * Variable and a block where it's coming from (last jump instruction), this variable is aliased + * by the succeeding label, which needs to be resolved first, before this one can be. */ class AliasPair { RAVariable variable; @@ -114,10 +107,8 @@ class AliasPair { } /** - * Map of variables are aliases for a list of variables - * used in predecessor jump instructions. First the - * successor label variable needs to be resolved and after - * the predecessor labels. + * Map of variables are aliases for a list of variables used in predecessor jump instructions. + * First the successor label variable needs to be resolved and after the predecessor labels. */ private Map> aliasMap; @@ -183,10 +174,9 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b } /** - * Resolves label variable locations by finding where they are first used. - * Walk back from their usage to their defining label (bottom-up), handling any - * spills, reloads and moves along the way to set the location in label back - * after register allocator strips this information. + * Resolves label variable locations by finding where they are first used. Walk back from their + * usage to their defining label (bottom-up), handling any spills, reloads and moves along the + * way to set the location in label back after register allocator strips this information. */ public void resolvePhiFromUsage() { Queue> worklist = new ArrayDeque<>(); @@ -248,8 +238,8 @@ public void resolvePhiFromUsage() { } /** - * Initialize first usages for variables, top-down in-order to - * collect all necessary information for the resolution. + * Initialize first usages for variables, top-down in-order to collect all necessary information + * for the resolution. */ protected void initializeUsages() { Queue> worklist = new ArrayDeque<>(); @@ -377,14 +367,12 @@ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction } /** - * Handle a register allocator inserted move, change - * locations of variables based on the locations. + * Handle a register allocator inserted move, change locations of variables based on the + * locations. * - * If a variable is in location reg1 and a move - * is found reg1 = MOVE reg2, then said variable - * will now be in reg2, because reg1 will now - * have different content when walking through - * the instructions in reverse. + * If a variable is in location reg1 and a move is found reg1 = MOVE reg2, then + * said variable will now be in reg2, because reg1 will now have different content + * when walking through the instructions in reverse. * * @param usage Variable locations for this block * @param from Source location @@ -408,9 +396,8 @@ protected void handleMove(BlockUsage usage, RAValue from, RAValue to) { } /** - * Resolve locations for all variables in a label, also - * mark first usage for aliased variables - variables - * used in predecessors that have no other usage. + * Resolve locations for all variables in a label, also mark first usage for aliased variables - + * variables used in predecessors that have no other usage. * * @param usage usage for the block we are resolving, contains the locations * @param label label we are resolving diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java index c2e58342da8f..2d47e7fc486c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java @@ -27,8 +27,8 @@ import jdk.vm.ci.code.Register; /** - * Invalid register was used in allocation - * as defined by the {@link jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig}. + * Invalid register was used in allocation as defined by the + * {@link jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig}. */ @SuppressWarnings("serial") public class InvalidRegisterUsedException extends RAVException { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java index 121c5ed0aeb1..31bf90688dec 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -42,10 +42,10 @@ public class KindsMismatchException extends RAVException { * Construct a KindsMismatchException. * * @param instruction Instruction where violation occurred - * @param block Block where violation occurred - * @param value1 First value in comparison, original variable. - * @param value2 Second value in comparison, either current location or value stored in state - * @param origVsCurr Comparing original variable to current location + * @param block Block where violation occurred + * @param value1 First value in comparison, original variable. + * @param value2 Second value in comparison, either current location or value stored in state + * @param origVsCurr Comparing original variable to current location */ public KindsMismatchException(LIRInstruction instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { super(KindsMismatchException.getErrorMessage(instruction, block, value1, value2, origVsCurr)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java index 89c1df939087..62c84ca4f529 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java @@ -36,8 +36,8 @@ public class MissingLocationError extends RAVError { * Construct a MissingLocationError. * * @param instruction Instruction where violation occurred - * @param block Block where violation occurred - * @param variable Variable before allocation, that has no location afterward + * @param block Block where violation occurred + * @param variable Variable before allocation, that has no location afterward */ public MissingLocationError(LIRInstruction instruction, BasicBlock block, RAValue variable) { super(MissingLocationError.getMessage(instruction, block, variable)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java index f783bd1a8c1e..63090a3cb82a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java @@ -31,9 +31,9 @@ import java.util.EnumSet; /** - * Value used in instruction does not satisfy it's {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag operand flags}, - * for example if an operand is a stack slot, but should only - * be a register. + * Value used in instruction does not satisfy it's + * {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag operand flags}, for example if an + * operand is a stack slot, but should only be a register. */ @SuppressWarnings("serial") public class OperandFlagMismatchException extends RAVException { @@ -43,11 +43,11 @@ public class OperandFlagMismatchException extends RAVException { public EnumSet flags; public OperandFlagMismatchException(RAVInstruction.Op op, BasicBlock block, Value value, EnumSet flags) { - super(getErrorMesage(op, block, value, flags)); - this.instruction = op; - this.block = block; - this.value = value; - this.flags = flags; + super(getErrorMesage(op, block, value, flags)); + this.instruction = op; + this.block = block; + this.value = value; + this.flags = flags; } static String getErrorMesage(RAVInstruction.Op op, BasicBlock block, Value value, EnumSet flags) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java index 3e85bcf3b71d..37bcd08db0f5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java @@ -28,8 +28,7 @@ import jdk.vm.ci.code.RegisterValue; /** - * Wrap around {@link RegisterValue} to only index - * by the name of the {@link Register} it holds. + * Wrap around {@link RegisterValue} to only index by the name of the {@link Register} it holds. */ public class RARegister extends RAValue { protected RegisterValue registerValue; @@ -64,8 +63,7 @@ public int hashCode() { } /** - * Equal RegisterValue on it's Register, not Register and kind, - * otherwise same as Value. + * Equal RegisterValue on it's Register, not Register and kind, otherwise same as Value. * * @param other The reference object with which to compare. * @return Are said values equal? diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java index ce66375cbc1f..43797caf0431 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVError.java @@ -27,9 +27,8 @@ import jdk.graal.compiler.debug.GraalError; /** - * An internal error occurred within the - * verification process, not caused by - * the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase register allocation}. + * An internal error occurred within the verification process, not caused by the + * {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase register allocation}. */ @SuppressWarnings("serial") public class RAVError extends GraalError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java index c73af721a3fb..6c58535da76c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java @@ -27,9 +27,9 @@ import jdk.graal.compiler.debug.GraalError; /** - * Register Allocation Verification Exception - - * a violation made by the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase register allocation} - * occurred and will be thrown in verification phase. + * Register Allocation Verification Exception - a violation made by the + * {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase register allocation} occurred and + * will be thrown in verification phase. */ @SuppressWarnings("serial") public class RAVException extends GraalError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java index 253d22a70845..ee1d715aff2d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -27,9 +27,9 @@ import java.util.List; /** - * Composite exception taking every {@link RAVException exception} - * that occurred (exceptions caused by the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase}) - * and combining them together to one exception. + * Composite exception taking every {@link RAVException exception} that occurred (exceptions caused + * by the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase}) and combining them together + * to one exception. */ @SuppressWarnings("serial") public class RAVFailedVerificationException extends RAVException { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 9061c0234bed..3af88ec8c79d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -54,27 +54,22 @@ public abstract static class Base { protected LIRInstruction lirInstruction; /** - * List of virtual moves to be inserted after this instruction, - * virtual moves are ones removed by the allocator still holding - * relevant information to the verification process, for example - * first label always uses registers (instead of variables) based on ABI, - * and a move is inserted indicating that those registers have certain - * variables. + * List of virtual moves to be inserted after this instruction, virtual moves are ones + * removed by the allocator still holding relevant information to the verification process, + * for example first label always uses registers (instead of variables) based on ABI, and a + * move is inserted indicating that those registers have certain variables. * - * @example [rsi, rbp] = LABEL - * v1 = MOVE rsi + * @example [rsi, rbp] = LABEL v1 = MOVE rsi */ protected List virtualMoveList; /** - * List of speculative moves, these might be removed, but still - * hold important information for us, so we add them to the verifier - * IR in-case they are, this happens when a MOVE source and target - * locations are equal (and thus redundant) but before allocation - * their variable counter-parts are not equal. + * List of speculative moves, these might be removed, but still hold important information + * for us, so we add them to the verifier IR in-case they are, this happens when a MOVE + * source and target locations are equal (and thus redundant) but before allocation their + * variable counter-parts are not equal. * - * @example before alloc: v1 = MOVE v2 - * after alloc: rax = MOVE rax + * @example before alloc: v1 = MOVE v2 after alloc: rax = MOVE rax */ protected List speculativeMoveList; @@ -188,7 +183,8 @@ public void addOrig(int index, Value value) { /** * Verify that all presumed values are present and that both sides have it. * - * @return true, if contents have been successfully verified, false if there's null in either array. + * @return true, if contents have been successfully verified, false if there's null in + * either array. */ public boolean verifyContents() { for (int i = 0; i < this.curr.length; i++) { @@ -237,8 +233,8 @@ public void setCurr(JavaValue[] curr) { } /** - * RAV instruction that handles a regular operation - * in an abstract way - we do not care about the function of said operation. + * RAV instruction that handles a regular operation in an abstract way - we do not care about + * the function of said operation. */ public static class Op extends Base { /** @@ -254,14 +250,12 @@ public static class Op extends Base { */ public ValueArrayPair temp; /** - * Pairs of inputs used by this operation - * that need to be alive after it completes. + * Pairs of inputs used by this operation that need to be alive after it completes. */ public ValueArrayPair alive; /** - * Pairs of values retrieved from LIRFrameState, - * verified same as any other input to make + * Pairs of values retrieved from LIRFrameState, verified same as any other input to make * sure GC has all necessary information. */ public ValueArrayPair stateValues; @@ -272,8 +266,8 @@ public static class Op extends Base { public ArrayList bcFrames; /** - * List of GC roots, calculated using LocationMarker class, - * other references in state maps need to be nullified. + * List of GC roots, calculated using LocationMarker class, other references in state maps + * need to be nullified. */ public List references; @@ -340,7 +334,7 @@ public boolean isJump() { } public boolean isCall() { - return lirInstruction instanceof AMD64Call.CallOp || lirInstruction instanceof AArch64Call.CallOp; + return lirInstruction instanceof AMD64Call.CallOp || lirInstruction instanceof AArch64Call.CallOp; } public boolean isSafePoint() { @@ -348,11 +342,9 @@ public boolean isSafePoint() { } /** - * Check if stateValues have null values, if so - * the state is not complete. This happens because - * iterating over certain values in LIRFrameState is - * ignored because they are a concrete stack slot and - * not a virtual one (StackLockValue). + * Check if stateValues have null values, if so the state is not complete. This happens + * because iterating over certain values in LIRFrameState is ignored because they are a + * concrete stack slot and not a virtual one (StackLockValue). * * @return true, if complete - non-null values after allocation */ @@ -377,8 +369,7 @@ public String toString() { } /** - * A move between to concrete locations - * inserted by the register allocator. + * A move between to concrete locations inserted by the register allocator. */ public static class LocationMove extends Base { public RAValue from; @@ -484,8 +475,8 @@ public String toString() { } /** - * Move a value or variable to a concrete location, - * inserted by allocator (materialization) or not (virtual move). + * Move a value or variable to a concrete location, inserted by allocator (materialization) or + * not (virtual move). */ public static class ValueMove extends Base { /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index b6caa94fe5c4..794bc0d3a0a0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -30,14 +30,13 @@ import jdk.vm.ci.meta.Value; /** - * Wrapper around Value to change how indexing - * in data structures like {@link java.util.Map} or {@link java.util.Set} is done. + * Wrapper around Value to change how indexing in data structures like {@link java.util.Map} or + * {@link java.util.Set} is done. * *

- * Values are indexed without their {@link LIRKind kind} - * associated with them, this is necessary for {@link AllocationStateMap} - * because locations can change kinds and still be associated - * with one key/value pair in said map. + * Values are indexed without their {@link LIRKind kind} associated with them, this is necessary for + * {@link AllocationStateMap} because locations can change kinds and still be associated with one + * key/value pair in said map. *

*/ public class RAValue { @@ -108,10 +107,9 @@ public int hashCode() { } /** - * Are two {@link RAValue values} equal? - * - check for offset for {@link jdk.vm.ci.code.StackSlot} - * - check for id for {@link jdk.graal.compiler.lir.VirtualStackSlot} - * - otherwise default to normal equals on {@link Value} + * Are two {@link RAValue values} equal? - check for offset for {@link jdk.vm.ci.code.StackSlot} + * - check for id for {@link jdk.graal.compiler.lir.VirtualStackSlot} - otherwise default to + * normal equals on {@link Value} * * @param other The reference object with which to compare. * @return Are said values equal? diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java index 6826f5c4c5c1..385967dd38db 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java @@ -27,12 +27,11 @@ import jdk.graal.compiler.lir.Variable; /** - * Wrapper around {@link Variable} to change how indexing - * in data structures like {@link java.util.Map} or {@link java.util.Set} is done. + * Wrapper around {@link Variable} to change how indexing in data structures like + * {@link java.util.Map} or {@link java.util.Set} is done. * *

- * We index only by the {@link Variable} index instead of - * including the kind as well. + * We index only by the {@link Variable} index instead of including the kind as well. *

*/ public class RAVariable extends RAValue { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java index 694a41cb366f..921f491f3495 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java @@ -45,16 +45,13 @@ import static jdk.vm.ci.code.ValueUtil.isStackSlot; /** - * Build references list for operations that can be - * used to in-validate references that are not part - * of it, to make sure GC-freed references are not - * used further. + * Build references list for operations that can be used to in-validate references that are not part + * of it, to make sure GC-freed references are not used further. * *

- * It uses the {@link LocationMarker} class that is used - * in the {@link jdk.graal.compiler.lir.phases.FinalCodeAnalysisPhase} where the actual - * set of references is built, we require this - * information earlier. + * It uses the {@link LocationMarker} class that is used in the + * {@link jdk.graal.compiler.lir.phases.FinalCodeAnalysisPhase} where the actual set of references + * is built, we require this information earlier. *

*/ public class ReferencesBuilder { @@ -150,8 +147,7 @@ private RegisterAttributes attributes(Register reg) { } /** - * Set references to the operation, if it can be found in - * the pre-allocation map. + * Set references to the operation, if it can be found in the pre-allocation map. * * @param instruction LIR instruction that holds these references * @param info LIR frame state diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 7daef2d30c3f..3351c91109b5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -35,15 +35,13 @@ import java.util.Queue; /** - * Class encapsulating the whole Register Allocation Verification. - * Maintaining entry states for blocks, resolving label variable - * locations and checking validity of every location to variable + * Class encapsulating the whole Register Allocation Verification. Maintaining entry states for + * blocks, resolving label variable locations and checking validity of every location to variable * correspondence. */ public class RegAllocVerifier { /** - * Verifier IR that abstracts LIR instructions - * and marks moves inserted by the allocator. + * Verifier IR that abstracts LIR instructions and marks moves inserted by the allocator. */ protected BlockMap> blockInstructions; @@ -58,15 +56,13 @@ public class RegAllocVerifier { protected LIR lir; /** - * Register Allocator config used for validating - * if valid register is used by the allocator. + * Register Allocator config used for validating if valid register is used by the allocator. */ protected RegisterAllocationConfig registerAllocationConfig; /** - * Resolves locations for label variables by finding - * their first usage and walking back to the defining - * label. + * Resolves locations for label variables by finding their first usage and walking back to the + * defining label. */ protected FromUsageResolverGlobal fromUsageResolverGlobal; @@ -75,24 +71,27 @@ public class RegAllocVerifier { */ protected ConflictResolver constantMaterializationConflictResolver; + protected RematerializationHandler rematerializationHandler; + public RegAllocVerifier(LIR lir, BlockMap> blockInstructions, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; this.registerAllocationConfig = registerAllocationConfig; - this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); - var cfg = lir.getControlFlowGraph(); this.blockInstructions = blockInstructions; this.blockEntryStates = new BlockMap<>(cfg); this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); + + var constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); + this.constantMaterializationConflictResolver = constantMaterializationConflictResolver; + this.rematerializationHandler = new RematerializationHandler(constantMaterializationConflictResolver); } /** - * For every block, we need to calculate its entry state - * which is a combination of states of blocks that are its - * predecessors, we get after reached a fixed point state, - * where no entry state is changed. + * For every block, we need to calculate its entry state which is a combination of states of + * blocks that are its predecessors, we get after reached a fixed point state, where no entry + * state is changed. * * This is necessary to verify instruction inputs correctly. */ @@ -140,11 +139,9 @@ protected BlockVerifierState createNewBlockState(BasicBlock block) { } /** - * By using the entry states calculated in step beforehand, - * we check input of every instruction to see that it matches - * symbols before allocation, after wards we update the state - * so the next instruction has correct state at said instruction - * input. + * By using the entry states calculated in step beforehand, we check input of every instruction + * to see that it matches symbols before allocation, after wards we update the state so the next + * instruction has correct state at said instruction input. */ public void verifyInstructionInputs() { for (var blockId : this.lir.getBlocks()) { @@ -188,7 +185,7 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { if (block.getSuccessorCount() == 0) { state.checkCalleeSavedRegisters(); } - } catch (RAVException e) { + } catch (RAVException e) { exceptions.add(e); } } @@ -199,17 +196,16 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { } /** - * Run verification based on verifier IR created in phase beforehand, - * resolving stripped label variable locations back, calculating entry state - * for every block so that at the end we can verify inputs of instructions - * match variables present before allocation. + * Run verification based on verifier IR created in phase beforehand, resolving stripped label + * variable locations back, calculating entry state for every block so that at the end we can + * verify inputs of instructions match variables present before allocation. * - * The issues we are looking to catch are mostly about making sure that - * order of spills, reloads and moves is correct and that used location - * after stores the symbol that is supposed to be there. + * The issues we are looking to catch are mostly about making sure that order of spills, reloads + * and moves is correct and that used location after stores the symbol that is supposed to be + * there. * - * We also make sure that kinds are still matching, operand flags aren't violated, - * alive location not being used as temp or output of same instruction. + * We also make sure that kinds are still matching, operand flags aren't violated, alive + * location not being used as temp or output of same instruction. */ public void run() { this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index c233c7095834..b1245704f835 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -60,23 +60,21 @@ import java.util.Set; /** - * Verification phase for Register Allocation, wraps around - * the actual allocator and validates that order of spills, reloads - * and moves is correct and that variables before allocation - * are actually stored in current locations chosen by the allocator. + * Verification phase for Register Allocation, wraps around the actual allocator and validates that + * order of spills, reloads and moves is correct and that variables before allocation are actually + * stored in current locations chosen by the allocator. + * *

* Needs to extend RegisterAllocationPhase to not throw an exception. + *

*/ public class RegAllocVerifierPhase extends RegisterAllocationPhase { public static class Options { - @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) - public static final OptionKey EnableRAVerifier = new OptionKey<>(false); + @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) public static final OptionKey EnableRAVerifier = new OptionKey<>(false); - @Option(help = "Verify output of stack allocator with register allocator", type = OptionType.Debug) - public static final OptionKey VerifyStackAllocator = new OptionKey<>(true); + @Option(help = "Verify output of stack allocator with register allocator", type = OptionType.Debug) public static final OptionKey VerifyStackAllocator = new OptionKey<>(true); - @Option(help = "Collect reference map information to verify", type = OptionType.Debug) - public static final OptionKey CollectReferences = new OptionKey<>(false); + @Option(help = "Collect reference map information to verify", type = OptionType.Debug) public static final OptionKey CollectReferences = new OptionKey<>(false); } /** @@ -85,8 +83,7 @@ public static class Options { protected RegisterAllocationPhase allocator; /** - * Stack allocator, if not null, being verified - * simultaneously with reg allocator. + * Stack allocator, if not null, being verified simultaneously with reg allocator. */ protected LIRPhase stackSlotAllocator; @@ -182,11 +179,13 @@ protected Map saveInstructionsPreAlloc(LIR RAVInstruction.Base previousInstr = null; for (var instruction : instructions) { if (this.isVirtualMove(instruction)) { - // Virtual moves (variable = MOV real register) are going to be removed by the allocator, - // but we still need the information about which variables are associated to which real - // registers, and so we store them. They are generally associated to other instructions - // that's why we append them here to the previous instruction (for example Label or Foreign Call) - // use these, if this instruction was deleted in the allocator, then they will be missing too. + // Virtual moves (variable = MOV real register) are going to be removed by the + // allocator, but we still need the information about which variables are + // associated to which real registers, and so we store them. + // They are generally associated to other instructions + // that's why we append them here to the previous instruction (for example Label + // or Foreign Call) use these, if this instruction was deleted in the allocator, + // then they will be missing too. assert previousInstr != null; var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); @@ -195,16 +194,19 @@ protected Map saveInstructionsPreAlloc(LIR var virtualMove = new RAVInstruction.ValueMove(instruction, variable, location); previousInstr.addVirtualMove(virtualMove); - continue; // No need to store virtual move here, it is stored into previous instruction. + + // No need to store virtual move here, it is stored into previous instruction. + continue; } boolean speculative = false; if (this.isSpeculativeMove(instruction)) { speculative = true; - // Speculative moves are in form ry = MOVE vx, which could be removed if variable - // ends up being allocated to the same register as ry. If it was removed - // we need to re-add it because it holds important information about where value of - // this variable is placed - for label resolution after the label. + // Speculative moves are in form ry = MOVE vx, which could be removed if + // variable ends up being allocated to the same register as ry. + // If it was removed we need to re-add it because it holds + // important information about where value of this variable + // is placed - for label resolution after the label. assert previousInstr != null; var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); @@ -257,9 +259,9 @@ public void doState(LIRFrameState state) { /** * Use information before allocation to verify output of allocator(s). * - * @param lir LIR + * @param lir LIR * @param preallocMap Map of instructions before allocation - * @param context Allocation context + * @param context Allocation context */ protected void verifyAllocation(LIR lir, Map preallocMap, AllocationContext context) { var instructions = getVerifierInstructions(lir, preallocMap, context); @@ -292,9 +294,8 @@ protected void verifyAllocation(LIR lir, Map> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext _ctx) { @@ -382,11 +383,10 @@ public void doState(LIRFrameState state) { } /** - * Fixes value move created before any allocation has happened, - * we mainly care about stack allocator running - old value move - * still keeps the virtual stack slot, but the underlying lir - * instruction already has an allocated concrete stack slot, so - * for verification to work correctly, it needs to be changed. + * Fixes value move created before any allocation has happened, we mainly care about stack + * allocator running - old value move still keeps the virtual stack slot, but the underlying lir + * instruction already has an allocated concrete stack slot, so for verification to work + * correctly, it needs to be changed. * * @param valueMove Old value move created before any (stack) allocation * @return Fixed value move @@ -414,12 +414,11 @@ protected RAVInstruction.ValueMove fixOldValueMove(RAVInstruction.ValueMove valu } /** - * Iterate over every instruction after allocation save it to a set - * to see if speculative moves should be re-added or not and also - * track if variable has been defined before. + * Iterate over every instruction after allocation save it to a set to see if speculative moves + * should be re-added or not and also track if variable has been defined before. * - * @param lir LIR - * @param preallocMap Map of instructions before allocation + * @param lir LIR + * @param preallocMap Map of instructions before allocation * @param definedVariables Output map, set defined variables here * @return Set of instructions present after allocation */ @@ -447,13 +446,13 @@ protected Set preprocessAllocatedInstructions(LIR lir, Map handleSpeculativeMoves(RAVInstruction.Op op, Set presentInstructions, Map definedVariables) { @@ -493,10 +492,12 @@ protected List handleSpeculativeMoves(RAVInstruction.O } /** - * Create Register Verifier Instruction that was created by the {@link RegisterAllocationPhase register allocator}. - * Generally speaking, it's always a move instruction, other ones return null. + * Create Register Verifier Instruction that was created by the {@link RegisterAllocationPhase + * register allocator}. Generally speaking, it's always a move instruction, other ones return + * null. * - * @param instruction LIR Instruction newly created by {@link RegisterAllocationPhase register allocator} + * @param instruction LIR Instruction newly created by {@link RegisterAllocationPhase register + * allocator} * @return Spill, Reload, Move or null if instruction is not a move */ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) { @@ -536,14 +537,13 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) } /** - * Determines if instruction is a virtual {@link RAVInstruction.ValueMove move}, a virtual move is - * a move instruction that moves a real register value into a variable, - * which is something that will always get removed from the final allocated - * IR. + * Determines if instruction is a virtual {@link RAVInstruction.ValueMove move}, a virtual move + * is a move instruction that moves a real register value into a variable, which is something + * that will always get removed from the final allocated IR. * *

- * This information is important to the verification process and needs to - * be part of the Verifier IR. + * This information is important to the verification process and needs to be part of the + * Verifier IR. *

* * @param instruction LIR instruction we are looking at @@ -560,12 +560,12 @@ protected boolean isVirtualMove(LIRInstruction instruction) { } /** - * Determines if a {@link RAVInstruction.ValueMove move} is speculative - it could potentially be - * removed, but hold important information to the verification process. + * Determines if a {@link RAVInstruction.ValueMove move} is speculative - it could potentially + * be removed, but hold important information to the verification process. * *

- * For example, this happens for a move between two variables and after - * allocation locations are equal, making the move redundant. + * For example, this happens for a move between two variables and after allocation locations are + * equal, making the move redundant. *

* * @param instruction {@link LIRInstruction instruction} we are looking at diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java index 6d719860bc27..e13e56788cde 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java @@ -25,8 +25,8 @@ package jdk.graal.compiler.lir.alloc.verifier; /** - * Re-materialized constant has wrong source (not a {@link RAVInstruction.ValueMove}), - * but either undefined or something different. + * Re-materialized constant has wrong source (not a {@link RAVInstruction.ValueMove}), but either + * undefined or something different. */ @SuppressWarnings("serial") public class RematerializedConstantSourceMissingError extends RAVError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index 57b056abb4bc..1214e0f95009 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -27,8 +27,7 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; /** - * Default allocation state for all locations, - * nothing was yet inserted. + * Default allocation state for all locations, nothing was yet inserted. */ public class UnknownAllocationState extends AllocationState { /** @@ -42,8 +41,8 @@ public boolean isUnknown() { } /** - * Meet state from predecessor, if both are unknown then unknown is returned, - * otherwise {@link ConflictedAllocationState conflict} occurs. + * Meet state from predecessor, if both are unknown then unknown is returned, otherwise + * {@link ConflictedAllocationState conflict} occurs. * * @param other Other state coming from a predecessor edge * @return {@link UnknownAllocationState Unknown} if both are, otherwise a conflict diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java index 076cfa96f67a..60be3f4b71b2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java @@ -29,9 +29,8 @@ import jdk.graal.compiler.lir.StandardOp; /** - * Unknown instruction was found after the allocation, - * this usually means that it's a different instruction - * from a move, and we do not know how to handle it. + * Unknown instruction was found after the allocation, this usually means that it's a different + * instruction from a move, and we do not know how to handle it. */ @SuppressWarnings("serial") public class UnknownInstructionError extends RAVError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index b1455ece2b2d..23ae580c8056 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -31,8 +31,9 @@ import jdk.vm.ci.meta.Value; /** - * Allocation state holding a single, concrete {@link Value} (wrapped with {@link RAValue}), - * also accompanied by {@link RAVInstruction instruction} and {@link BasicBlock block} where it was created. + * Allocation state holding a single, concrete {@link Value} (wrapped with {@link RAValue}), also + * accompanied by {@link RAVInstruction instruction} and {@link BasicBlock block} where it was + * created. */ public class ValueAllocationState extends AllocationState implements Cloneable { protected RAValue value; @@ -65,9 +66,9 @@ public ValueAllocationState(ValueAllocationState other) { } /** - * Create an illegal value allocation state, used - * as a substitute for {@link UnknownAllocationState unknown} when creating - * a {@link ConflictedAllocationState conflict}. + * Create an illegal value allocation state, used as a substitute for + * {@link UnknownAllocationState unknown} when creating a {@link ConflictedAllocationState + * conflict}. * * @return instance of {@link ValueAllocationState} holding {@link Value#ILLEGAL}. */ @@ -92,14 +93,15 @@ public BasicBlock getBlock() { } /** - * Meet a state from predecessor block, if it's {@link ValueAllocationState} - * and contents are equal, then same state is returned, otherwise - * a {@link ConflictedAllocationState conflict} is created between said states. + * Meet a state from predecessor block, if it's {@link ValueAllocationState} and contents are + * equal, then same state is returned, otherwise a {@link ConflictedAllocationState conflict} is + * created between said states. * * @param other Other state coming from a predecessor edge * @param otherBlock Where the other state is coming from * @param currBlock Where the current state is coming from - * @return {@link ValueAllocationState} if their contents are equal, otherwise {@link ConflictedAllocationState}. + * @return {@link ValueAllocationState} if their contents are equal, otherwise + * {@link ConflictedAllocationState}. */ @Override public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock currBlock) { @@ -118,7 +120,8 @@ public AllocationState meet(AllocationState other, BasicBlock otherBlock, Bas } var otherValueAllocState = (ValueAllocationState) other; - if (!this.value.equals(otherValueAllocState.getRAValue())) { // Does not take kind into account. + if (!this.value.equals(otherValueAllocState.getRAValue())) { + // Does not take kind into account. return new ConflictedAllocationState(this, otherValueAllocState); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index f630fc470131..129dbd51e451 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -42,10 +42,10 @@ public class ValueNotInRegisterException extends RAVException { * Construct a ValueNotInRegisterException. * * @param instruction Instruction where violation occurred - * @param block Block where violation occurred - * @param variable Target varible we are looking for - * @param location Location where we couldn't find it - * @param state The actual state that the location is in + * @param block Block where violation occurred + * @param variable Target varible we are looking for + * @param location Location where we couldn't find it + * @param state The actual state that the location is in */ public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock block, RAValue variable, RAValue location, AllocationState state) { super(ValueNotInRegisterException.getErrorMessage(instruction, block, variable, location, state)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index c7953db9cd2a..57880403b5b3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -33,11 +33,10 @@ public class VerifierPrinter { /** - * Print human-readable representation of the Verifier IR - * to an output stream. + * Print human-readable representation of the Verifier IR to an output stream. * - * @param out Output stream - * @param lir LIR + * @param out Output stream + * @param lir LIR * @param instructions Verifier IR */ public static void print(PrintStream out, LIR lir, BlockMap> instructions) { From 0b6de17b9fea66d5d12aba45db25c5c89d436591 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 18 Mar 2026 14:26:09 +0100 Subject: [PATCH 078/112] Run style check again --- .../core/test/RegAllocVerifierTest.java | 58 +++++++++---------- .../verifier/FromUsageResolverGlobal.java | 43 ++++++++------ .../lir/alloc/verifier/RegAllocVerifier.java | 6 +- .../alloc/verifier/RegAllocVerifierPhase.java | 4 +- 4 files changed, 57 insertions(+), 54 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index eadff75e8844..ecb4bd8408d1 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -77,8 +77,7 @@ public class RegAllocVerifierTest extends GraalCompilerTest { RAVException exception; /** - * Phase that causes RAVException to be thrown, - * by modifying LIR or Verifier State. + * Phase that causes RAVException to be thrown, by modifying LIR or Verifier State. */ RAVPhaseWrapper phase; @@ -98,9 +97,8 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo } /** - * Overwrites a destination variable with a newly created one - * to cause a ValueNotInLocationException with old variable being - * in said place. + * Overwrites a destination variable with a newly created one to cause a + * ValueNotInLocationException with old variable being in said place. */ class ChangeVariablePhase extends RAVPhaseWrapper { protected RAVariable originalVariable; @@ -157,9 +155,8 @@ protected RAVariable createNewVariable(LIR lir, RAVariable oldVariable) { } /** - * This pass changes register allocation config to only allow certain - * registers to be used for allocation, and we want the verifier to - * detect usage of said register. + * This pass changes register allocation config to only allow certain registers to be used for + * allocation, and we want the verifier to detect usage of said register. */ class DisallowedRegisterPhase extends RAVPhaseWrapper { protected Register ignoredReg; @@ -237,8 +234,8 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet(); } - @Override protected BlockMap> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { modifyLIR(lir, instrMap, context); @@ -371,9 +367,8 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet - * Finds the first instruction that satisfies having - * both alive operand and temp/output and changes it - * so one location is the same. + * Finds the first instruction that satisfies having both alive operand and temp/output and + * changes it so one location is the same. + *

*/ abstract class ViolateAliveConstraint extends RAVPhaseWrapper { class SetAliveRegProc implements ValueProcedure { @@ -581,10 +576,9 @@ protected Map saveInstructionsPreAlloc(LIR protected abstract BasicBlock getConflictUseBlock(LIR lir); /** - * Get block where conflict will be created, - * by inserting a ValueMove instruction. + * Get block where conflict will be created, by inserting a ValueMove instruction. * - * @param lir LIR + * @param lir LIR * @param conflictBlock Block where conflict will be used * @return Source of the conflict */ @@ -633,11 +627,11 @@ protected RAVariable getUsedVariableFromBlock(LIR lir, BasicBlock block, Map< /** * Adds a conflict inducing value move to a block. * - * @param lir LIR - * @param block Block where we are putting move to - * @param instrMap Pre allocation instruction map + * @param lir LIR + * @param block Block where we are putting move to + * @param instrMap Pre allocation instruction map * @param targetVariable Target variable we are creating conflict with - * @param variables Variables and their locations from previous steps + * @param variables Variables and their locations from previous steps */ protected void addVirtualMove(LIR lir, BasicBlock block, Map instrMap, RAVariable targetVariable, Map variables) { var instructions = lir.getLIRforBlock(block); @@ -883,7 +877,8 @@ public void testInvalidRegisterUsed() { assertException(InvalidRegisterUsedException.class); var iruException = (InvalidRegisterUsedException) exception; - Assert.assertEquals(iruException.register, disallowedRegPhase.ignoredReg); // Used forbidden register + Assert.assertEquals(iruException.register, disallowedRegPhase.ignoredReg); // Used forbidden + // register } @Test @@ -901,9 +896,12 @@ public void testWrongVariableInState() { assertException(ValueNotInRegisterException.class); var vnrException = (ValueNotInRegisterException) exception; - Assert.assertEquals(changeVariablePhase.originalVariable, vnrException.variable); // Expected original variable + + // Expected original variable + Assert.assertEquals(changeVariablePhase.originalVariable, vnrException.variable); Assert.assertTrue(vnrException.state instanceof ValueAllocationState); - Assert.assertEquals(changeVariablePhase.newVariable, ((ValueAllocationState) vnrException.state).getRAValue()); // But new variable is there instead + // But new variable is there instead + Assert.assertEquals(changeVariablePhase.newVariable, ((ValueAllocationState) vnrException.state).getRAValue()); } @Test diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index a48a68b59a5c..d9bb0b862434 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -42,17 +42,17 @@ /** * Resolve variables phi variables back to labels and jumps by find their first usage and handling * any reg allocator inserted moves back to the defining label. - * + *

* Register allocator strips us of this information that is necessary for the verification. In order * to avoid modifying the existing allocators, we rather try to resolve this information from other * instructions. - * + *

* Variables with only usage in jump instructions are marked as aliases and are resolved after their * successors. - * + *

* If a variable has no usage, then no location is resolved and verification continues without * issues. - * + *

* In the case the first usage of a label-defined variable is wrong, then JUMP instructions from * predecessors fail the verification - wrong register will be chosen. */ @@ -136,24 +136,36 @@ private BlockUsage(BlockUsage blockDefs) { this.locations = new EconomicHashMap<>(blockDefs.locations); } - private BlockUsage merge(BlockUsage other) { - var newDefs = new BlockUsage(this); - newDefs.reached.addAll(other.reached); - + /** + * Two blocks meet, a successor merges into it's predecessor to pass in newly reached + * variable and locations. + * + * @param other Other block usage information - the successor + * @return Has the current block (predecessor) been changed? + */ + private boolean meetWith(BlockUsage other) { + int reachedBefore = reached.size(); + reached.addAll(other.reached); + + boolean changed = reachedBefore != reached.size(); for (RAVariable variable : other.locations.keySet()) { var defValue = other.locations.get(variable); - if (defValue == null) { + if (defValue == null || defValue.isIllegal()) { continue; } - if (defValue.isIllegal() && newDefs.locations.containsKey(variable)) { + if (defValue.isIllegal() && locations.containsKey(variable)) { continue; } - newDefs.locations.put(variable, defValue); + if (locations.containsKey(variable) && locations.get(variable) == defValue) { + continue; + } + + locations.put(variable, defValue); } - return newDefs; + return changed; } } @@ -223,10 +235,7 @@ public void resolvePhiFromUsage() { this.blockUsageMap.put(pred, new BlockUsage(usage)); } else { var predReached = this.blockUsageMap.get(pred); - var newReached = predReached.merge(usage); - - this.blockUsageMap.put(pred, newReached); - if (predReached.reached.size() == newReached.reached.size()) { + if (!predReached.meetWith(usage)) { continue; } } @@ -369,7 +378,7 @@ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction /** * Handle a register allocator inserted move, change locations of variables based on the * locations. - * + *

* If a variable is in location reg1 and a move is found reg1 = MOVE reg2, then * said variable will now be in reg2, because reg1 will now have different content * when walking through the instructions in reverse. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 3351c91109b5..bfa9ee72c473 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -71,8 +71,6 @@ public class RegAllocVerifier { */ protected ConflictResolver constantMaterializationConflictResolver; - protected RematerializationHandler rematerializationHandler; - public RegAllocVerifier(LIR lir, BlockMap> blockInstructions, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; this.registerAllocationConfig = registerAllocationConfig; @@ -83,9 +81,7 @@ public RegAllocVerifier(LIR lir, BlockMap> blockInstru this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); - var constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); - this.constantMaterializationConflictResolver = constantMaterializationConflictResolver; - this.rematerializationHandler = new RematerializationHandler(constantMaterializationConflictResolver); + this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); } /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index b1245704f835..32f60fe72d5a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -310,10 +310,10 @@ protected RegisterAllocationConfig getRegisterAllocationConfig(AllocationContext * * @param lir LIR * @param preallocMap Pre-allocation map to keep track of virtual values - * @param _ctx Context of the allocation, kept here so tests can access this value here + * @param ctx Context of the allocation, kept here so tests can access this value here * @return Verifier IR */ - protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext _ctx) { + protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { Map definedVariables = new EconomicHashMap<>(); var presentInstructions = preprocessAllocatedInstructions(lir, preallocMap, definedVariables); From 6cfce85688c7facb3fedd4d77d866bba4adefe52 Mon Sep 17 00:00:00 2001 From: glencoco Date: Wed, 18 Mar 2026 14:45:36 +0100 Subject: [PATCH 079/112] Run the formatter again --- .../graal/compiler/core/test/RegAllocVerifierTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index ecb4bd8408d1..5c0ea034055d 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -234,8 +234,8 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet Date: Wed, 18 Mar 2026 15:22:03 +0100 Subject: [PATCH 080/112] Remove hotspot instructions --- .../compiler/lir/alloc/verifier/RAVInstruction.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 3af88ec8c79d..9bdd1e9eda62 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -24,14 +24,10 @@ */ package jdk.graal.compiler.lir.alloc.verifier; -import jdk.graal.compiler.hotspot.aarch64.AArch64HotSpotSafepointOp; -import jdk.graal.compiler.hotspot.amd64.AMD64HotSpotSafepointOp; import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.VirtualStackSlot; -import jdk.graal.compiler.lir.aarch64.AArch64Call; -import jdk.graal.compiler.lir.amd64.AMD64Call; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.meta.JavaKind; @@ -333,14 +329,6 @@ public boolean isJump() { return lirInstruction instanceof StandardOp.JumpOp; } - public boolean isCall() { - return lirInstruction instanceof AMD64Call.CallOp || lirInstruction instanceof AArch64Call.CallOp; - } - - public boolean isSafePoint() { - return references != null && (lirInstruction instanceof AMD64HotSpotSafepointOp || lirInstruction instanceof AArch64HotSpotSafepointOp || isCall()); - } - /** * Check if stateValues have null values, if so the state is not complete. This happens * because iterating over certain values in LIRFrameState is ignored because they are a From ac7a94e199c714645b550946cf7411ebc3283af4 Mon Sep 17 00:00:00 2001 From: glencoco Date: Sat, 21 Mar 2026 15:36:32 +0100 Subject: [PATCH 081/112] Verify references list --- .../alloc/verifier/BlockVerifierState.java | 54 +++++++++++++++---- .../verifier/MissingReferenceException.java | 47 ++++++++++++++++ .../lir/alloc/verifier/ReferencesBuilder.java | 4 ++ .../alloc/verifier/ValueAllocationState.java | 4 ++ 4 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 7ffce23124a7..1fa386cf06ec 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -287,21 +287,57 @@ public void check(RAVInstruction.Base instruction) { checkBytecodeFrames(op); - // We do not check the list of references if the state map - // actually has a reference inside it for said location, - // because there seems to be more cases of that simply - // not being true, either backup slots in stack moves - // are not yet defined / have different lir kind - // or the not marked as a reference - usually related to - // rbp in stack. + if (op.references != null) { + checkReferences(op); + } } else if (instruction instanceof RAVInstruction.LocationMove move) { checkMoveKinds(move); } } /** - * Check if the destination of a move has the correct type to store the - * {@link ValueAllocationState value}. + * Iterate over references collected by ReferenceBuilder and check if said locations actually + * contain a reference in the state map. + * + * @param op Operation containing references for GC + */ + protected void checkReferences(RAVInstruction.Op op) { + // ReferenceSet makes sure that contents are references only + for (RAValue reference : op.references) { + var state = values.get(reference); + if (state.isConflicted()) { + var confState = (ConflictedAllocationState) state; + for (var valAllocState : confState.getConflictedStates()) { + if (valAllocState.isIllegal()) { + continue; // Undefined in branch + } + + if (!valAllocState.getRAValue().getLIRKind().isValue()) { + continue; // Is a reference + } + + throw new MissingReferenceException(reference, state, op, block); + } + + continue; + } + + if (state.isUnknown()) { + throw new MissingReferenceException(reference, state, op, block); + } + + var valAllocState = (ValueAllocationState) state; + if (!valAllocState.getRAValue().getLIRKind().isValue()) { + continue; // Is a reference + } + + throw new MissingReferenceException(reference, state, op, block); + } + } + + /** + * Check that a move destination has the correct kind to store the {@link ValueAllocationState + * value}. * * @param move Move between locations, inserted by register allocator */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java new file mode 100644 index 000000000000..5e6c03a5ac32 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; + +@SuppressWarnings("serial") +public class MissingReferenceException extends RAVException { + public RAValue reference; + public AllocationState state; + public RAVInstruction.Op op; + public BasicBlock block; + + public MissingReferenceException(RAValue reference, AllocationState state, RAVInstruction.Op op, BasicBlock block) { + super(getMessage(reference, state, op, block)); + this.reference = reference; + this.state = state; + this.op = op; + this.block = block; + } + + public static String getMessage(RAValue reference, AllocationState state, RAVInstruction.Op op, BasicBlock block) { + return "Missing reference in " + reference + " in " + op.lirInstruction + " in " + block + " actually is " + state; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java index 921f491f3495..b8cb99cefa48 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java @@ -68,6 +68,10 @@ class ReferenceSet extends ValueSet { @Override public void put(Value v) { + if (v.getValueKind(LIRKind.class).isValue()) { + return; + } + references.add(RAValue.create(v)); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 23ae580c8056..bdb3c92125f6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -76,6 +76,10 @@ public static ValueAllocationState createIllegal(BasicBlock block) { return new ValueAllocationState(new RAValue(Value.ILLEGAL), null, block); } + public boolean isIllegal() { + return value.isIllegal(); + } + public Value getValue() { return value.getValue(); } From 6a91b75e66051aa5aa5b536ce65fac32de06d519 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 23 Mar 2026 12:55:39 +0100 Subject: [PATCH 082/112] Fix derived refs and native pointers --- .../replacements/test/DerivedOopTest.java | 9 -- .../test/PointerTrackingTest.java | 9 -- .../alloc/verifier/AllocationStateMap.java | 12 ++ .../alloc/verifier/BlockVerifierState.java | 141 +++++++++++++----- .../alloc/verifier/RegAllocVerifierPhase.java | 3 +- 5 files changed, 118 insertions(+), 56 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/DerivedOopTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/DerivedOopTest.java index 0b8eeb4dac3b..f1141b5e6584 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/DerivedOopTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/DerivedOopTest.java @@ -27,9 +27,6 @@ import java.util.Objects; import org.graalvm.word.impl.Word; -import jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase; -import jdk.graal.compiler.lir.phases.LIRSuites; -import jdk.graal.compiler.options.OptionValues; import org.hamcrest.MatcherAssert; import org.hamcrest.core.StringContains; import org.junit.Assert; @@ -107,12 +104,6 @@ public boolean equals(Object obj) { } } - @Override - protected LIRSuites createLIRSuites(OptionValues opts) { - var newOptions = new OptionValues(opts, RegAllocVerifierPhase.Options.EnableRAVerifier, false); - return super.createLIRSuites(newOptions); - } - @Test public void testFieldOffset() { // Run a couple times to encourage objects to move diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/PointerTrackingTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/PointerTrackingTest.java index 13cbe78de8bc..cc6aa83d4059 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/PointerTrackingTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/PointerTrackingTest.java @@ -24,9 +24,6 @@ */ package jdk.graal.compiler.replacements.test; -import jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase; -import jdk.graal.compiler.lir.phases.LIRSuites; -import jdk.graal.compiler.options.OptionValues; import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -55,12 +52,6 @@ public void before() { Assume.assumeTrue("doesn't aggressively move objects", runtime.getGarbageCollector() != HotSpotGraalRuntime.HotSpotGC.Shenandoah); } - @Override - protected LIRSuites createLIRSuites(OptionValues opts) { - var newOptions = new OptionValues(opts, RegAllocVerifierPhase.Options.EnableRAVerifier, false); - return super.createLIRSuites(newOptions); - } - @Test public void testTracking() { Result result = executeActual(getResolvedJavaMethod("trackingSnippet"), null); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java index 128e611c4b9a..c39699c8a66f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java @@ -24,10 +24,12 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; +import jdk.vm.ci.meta.ValueKind; import java.util.Map; import java.util.Set; @@ -52,6 +54,12 @@ public class AllocationStateMap { */ protected Map internalMap; + /** + * Map of casts for locations that was forced by allocator-inserted move, see + * {@link BlockVerifierState#isMoveKindChange}. + */ + protected Map> castMap; + /** * Register allocation config describing which registers can be used. */ @@ -59,12 +67,14 @@ public class AllocationStateMap { public AllocationStateMap(BasicBlock block, RegisterAllocationConfig registerAllocationConfig) { internalMap = new EconomicHashMap<>(); + castMap = new EconomicHashMap<>(); this.block = block; this.registerAllocationConfig = registerAllocationConfig; } public AllocationStateMap(BasicBlock block, AllocationStateMap other) { internalMap = new EconomicHashMap<>(other.internalMap); + castMap = new EconomicHashMap<>(other.castMap); registerAllocationConfig = other.registerAllocationConfig; this.block = block; } @@ -110,6 +120,7 @@ public void put(RAValue key, AllocationState state) { */ public void putWithoutRegCheck(RAValue key, AllocationState state) { internalMap.put(key, state); + castMap.remove(key); // Always remove the cast when new value is inserted. } /** @@ -170,6 +181,7 @@ public boolean mergeWith(AllocationStateMap source) { this.putWithoutRegCheck(entry.getKey(), result); } + castMap.putAll(source.castMap); // This should not affect the merge logic return changed; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 1fa386cf06ec..9bc7c41b04c5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -29,8 +29,11 @@ import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.CastValue; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.dfa.LocationMarker; +import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.JavaKind; @@ -150,16 +153,15 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { throw new MissingLocationError(op.lirInstruction, block, orig); } - if (!kindsEqual(orig, curr)) { - if (!op.isJump()) { - throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); - } + ValueKind currKind = curr.getValue().getValueKind(); + if (values.castMap.containsKey(curr)) { + // The current location might have been cast by a previous move + // see isMoveKindChange comment + currKind = values.castMap.get(curr); + } - // Skip when jump due to this case: - // "rdx|QWORD[*] = MOVE input: rdx|QWORD[.+] moveKind: QWORD" - // this move is inserted by the allocator and changes type - // of rdx from [.+] (compressed reference) (same as original variable) - // to [*] (invalid reference) + if (!kindsEqual(orig.getValue().getValueKind(), currKind)) { + throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); } AllocationState state = this.values.get(curr); @@ -222,17 +224,20 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { throw GraalError.shouldNotReachHere("Invalid state " + state); } + protected boolean kindsEqual(RAValue orig, RAValue curr) { + var origKind = orig.getValue().getValueKind(); + var currKind = curr.getValue().getValueKind(); + return kindsEqual(origKind, currKind); + } + /** * Are kinds equal even when {@link LIRKindWithCast casting} is present? * - * @param orig Original variable - * @param curr Current location + * @param origKind Original variable kind + * @param currKind Current location kind * @return Are they equal? */ - protected boolean kindsEqual(RAValue orig, RAValue curr) { - var origKind = orig.getValue().getValueKind(); - var currKind = curr.getValue().getValueKind(); - + protected boolean kindsEqual(ValueKind origKind, ValueKind currKind) { if (origKind instanceof LIRKindWithCast castKind) { origKind = castKind.getActualKind(); } @@ -245,7 +250,7 @@ protected boolean kindsEqual(RAValue orig, RAValue curr) { } /** - * Are kinds equal even when {@link jdk.graal.compiler.lir.CastValue cast value} is present? + * Are kinds equal even when {@link CastValue cast value} is present? * *

* We need to ignore the cast value because the currently stored value will not be cast. @@ -346,16 +351,67 @@ protected void checkMoveKinds(RAVInstruction.LocationMove move) { if (state instanceof ValueAllocationState valueAllocationState) { RAValue movedValue = valueAllocationState.getRAValue(); if (!kindsEqual(movedValue, move.to)) { + if (isMoveKindChange(move, valueAllocationState)) { + // This move changes the kind for destination location + // see isMoveKindChange comment + return; + } + throw new KindsMismatchException(move.lirInstruction, block, move.to, movedValue, false); } } } /** - * Check {@link jdk.vm.ci.code.BytecodeFrame frames}, before and after allocation, mainly - * checking that {@link LIRKind} is a reference when {@link JavaKind} is an Object and - * wise-versa, checking that {@link LIRKind} is not a reference when {@link JavaKind} is not an - * object. + * Does this move change the ValueKind? This happens for when derived reference gets changed to + * a normal reference, we make sure that the underlying platform kind is equal, and then allow + * change from derived ([.+]) to normal reference ([*]). + * + *

+ * Currently only allowed if the moved state is {@link ValueAllocationState}. + *

+ * + *

+ * The standard setup is as this: + * + *

+     * (v8|QWORD[.+] -> rcx|QWORD[.+]) = ADD (x: rcx|QWORD, y: r8|QWORD[.]) size: QWORD
+     * rdx|QWORD[*] = MOVE input: rcx|QWORD[.+] moveKind: QWORD // MoveResolver resolve mapping
+     * JUMP ~outgoingValues: [v8|QWORD[.+] -> rdx|QWORD[*]] destination: B1 -> B3 isThreadedJump: false
+     * 
+ * + * Add calculates the derived reference address, move casts it to LIRKind ref and value is used + * in the JUMP to the next block, where pre-allocation symbol (variable) has derived ref, but + * location has reference. + *

+ * + * This is then changed in {@link BlockVerifierState#checkOperand} to verify that the kind is + * correct. + * + * @param move Move that facilitates the change + * @param state Current value state this happens for + * @return if this move changes the kinds + */ + protected boolean isMoveKindChange(RAVInstruction.LocationMove move, ValueAllocationState state) { + var moveValueKind = state.getRAValue().getLIRKind(); + var toKind = move.to.getLIRKind(); + var fromKind = move.from.getLIRKind(); + + if (!moveValueKind.getPlatformKind().equals(toKind.getPlatformKind())) { + return false; + } + + if (!moveValueKind.getPlatformKind().equals(fromKind.getPlatformKind())) { + return false; + } + + return moveValueKind.isDerivedReference() && !toKind.isValue() && fromKind.isDerivedReference(); + } + + /** + * Check {@link BytecodeFrame frames}, before and after allocation, mainly checking that + * {@link LIRKind} is a reference when {@link JavaKind} is an Object and wise-versa, checking + * that {@link LIRKind} is not a reference when {@link JavaKind} is not an object. * * @param op Operation holding said frames * @throws RAVException when a violation occurs @@ -374,6 +430,11 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { } var kind = frame.kinds[i]; + if (JavaKind.Long.equals(kind)) { + // Skipping long(s) because it can be a numeric value + // or a derived reference / native pointer + continue; + } var origLIRKind = orig.getValueKind(LIRKind.class); var currLIRKind = curr.getValueKind(LIRKind.class); @@ -385,13 +446,11 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { throw new RAVException(orig + " -> " + curr + " not an object java kind when marked as a reference"); } else { if (origLIRKind.isValue() && currLIRKind.isValue()) { + // Either not a reference, or a derived one - which might not be marked as + // Object continue; } - // These two tests needed to be modified - // PointerTrackingTest - // jdk.graal.compiler.replacements.test.DerivedOopTest - // so this verification method doesn't throw an error - // when running with them + throw new RAVException(orig + " -> " + curr + " is a reference when not marked as an object java kind"); } } @@ -451,8 +510,8 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction) { /** * Make sure concrete current locations changed by the allocator are not violating set of - * {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag flags}, which specify what type can - * they be. This is done on every array of pairs (dest, uses, alive, temp). + * {@link LIRInstruction.OperandFlag flags}, which specify what type can they be. This is done + * on every array of pairs (dest, uses, alive, temp). * * @param valuePairs Value array pair we are verifying * @param op Instruction which holds this array, for tracing in exceptions @@ -500,15 +559,26 @@ public void update(RAVInstruction.Base instruction) { switch (instruction) { case RAVInstruction.Op op -> this.updateWithOp(op); case RAVInstruction.ValueMove virtMove -> this.updateWithValueMove(virtMove); - case RAVInstruction.LocationMove move -> { - if (move instanceof RAVInstruction.StackMove stackMove) { - // Maybe the backup slot should hold what the scratch register holds? - this.values.put(stackMove.backupSlot, UnknownAllocationState.INSTANCE); - } + case RAVInstruction.LocationMove move -> this.updateWithLocationMove(move); + default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); + } + } - this.values.putClone(move.to, this.values.get(move.from)); + public void updateWithLocationMove(RAVInstruction.LocationMove move) { + if (move instanceof RAVInstruction.StackMove stackMove) { + // Maybe the backup slot should hold what the scratch register holds? + this.values.put(stackMove.backupSlot, UnknownAllocationState.INSTANCE); + } + + var state = this.values.get(move.from); + + this.values.putClone(move.to, state); + + if (state instanceof ValueAllocationState valueAllocationState) { + var movedValue = valueAllocationState.getRAValue(); + if (!kindsEqual(movedValue, move.to) && isMoveKindChange(move, valueAllocationState)) { + this.values.castMap.put(move.to, move.from.getLIRKind()); // Add a new cast } - default -> throw GraalError.shouldNotReachHere("Invalid RAV instruction " + instruction); } } @@ -572,8 +642,7 @@ protected void updateWithOp(RAVInstruction.Op op) { * use. * *

- * References need to be retrieved using {@link jdk.graal.compiler.lir.dfa.LocationMarker} - * classes. + * References need to be retrieved using {@link LocationMarker} classes. *

* * @param op SafePoint we are using to remove old references diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 32f60fe72d5a..920f4bd4efc6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -275,8 +275,7 @@ protected void verifyAllocation(LIR lir, Map Date: Mon, 23 Mar 2026 13:23:44 +0100 Subject: [PATCH 083/112] Fix warnings from gate --- .../alloc/verifier/BlockVerifierState.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 9bc7c41b04c5..bb9326ab0c68 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -233,17 +233,23 @@ protected boolean kindsEqual(RAValue orig, RAValue curr) { /** * Are kinds equal even when {@link LIRKindWithCast casting} is present? * - * @param origKind Original variable kind - * @param currKind Current location kind + * @param origInputKind Original variable kind + * @param currInputKind Current location kind * @return Are they equal? */ - protected boolean kindsEqual(ValueKind origKind, ValueKind currKind) { - if (origKind instanceof LIRKindWithCast castKind) { + protected boolean kindsEqual(ValueKind origInputKind, ValueKind currInputKind) { + ValueKind origKind; + if (origInputKind instanceof LIRKindWithCast castKind) { origKind = castKind.getActualKind(); + } else { + origKind = origInputKind; } - if (currKind instanceof LIRKindWithCast castKind) { + ValueKind currKind; + if (currInputKind instanceof LIRKindWithCast castKind) { currKind = castKind.getActualKind(); + } else { + currKind = currInputKind; } return currKind.equals(origKind); @@ -510,8 +516,8 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction) { /** * Make sure concrete current locations changed by the allocator are not violating set of - * {@link LIRInstruction.OperandFlag flags}, which specify what type can they be. This is done - * on every array of pairs (dest, uses, alive, temp). + * {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag flags}, which specify what type can + * they be. This is done on every array of pairs (dest, uses, alive, temp). * * @param valuePairs Value array pair we are verifying * @param op Instruction which holds this array, for tracing in exceptions From 2bfa98759043ea42cb8a11722eee4e0e5cca8ad6 Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 23 Mar 2026 15:57:37 +0100 Subject: [PATCH 084/112] Enable verifier by default for tests --- .../compiler/lir/alloc/verifier/RegAllocVerifierPhase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 920f4bd4efc6..30e1478b27e3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -70,11 +70,11 @@ */ public class RegAllocVerifierPhase extends RegisterAllocationPhase { public static class Options { - @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) public static final OptionKey EnableRAVerifier = new OptionKey<>(false); + @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) public static final OptionKey EnableRAVerifier = new OptionKey<>(true); @Option(help = "Verify output of stack allocator with register allocator", type = OptionType.Debug) public static final OptionKey VerifyStackAllocator = new OptionKey<>(true); - @Option(help = "Collect reference map information to verify", type = OptionType.Debug) public static final OptionKey CollectReferences = new OptionKey<>(false); + @Option(help = "Collect reference map information to verify", type = OptionType.Debug) public static final OptionKey CollectReferences = new OptionKey<>(true); } /** From 1859d381dad0fa8bdff156baf507f5d48394f48e Mon Sep 17 00:00:00 2001 From: glencoco Date: Mon, 23 Mar 2026 18:01:25 +0100 Subject: [PATCH 085/112] Add synonym map for weird virtual move cases --- .../alloc/verifier/BlockVerifierState.java | 14 +++- .../lir/alloc/verifier/RegAllocVerifier.java | 6 +- .../alloc/verifier/VariableSynonymMap.java | 71 +++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index bb9326ab0c68..532d9d359e47 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -66,10 +66,13 @@ public class BlockVerifierState { */ protected BasicBlock block; - public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver) { + protected VariableSynonymMap synonymMap; + + public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver, VariableSynonymMap synonymMap) { this.values = new AllocationStateMap(block, registerAllocationConfig); this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; + this.synonymMap = synonymMap; this.block = block; } @@ -77,6 +80,7 @@ protected BlockVerifierState(BasicBlock block, BlockVerifierState other) { this.registerAllocationConfig = other.registerAllocationConfig; this.conflictConstantResolver = other.conflictConstantResolver; this.values = new AllocationStateMap(block, other.values); + this.synonymMap = other.synonymMap; this.block = block; } @@ -211,6 +215,10 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } } + if (orig.isVariable() && valAllocState.value.isVariable() && synonymMap.isSynonymOf(orig.asVariable(), valAllocState.value.asVariable())) { + return; // Resolved! + } + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } @@ -748,6 +756,10 @@ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove) { for (var location : locations) { this.values.put(location, new ValueAllocationState(valueMove.getLocation(), valueMove, block)); } + + if (valueMove.variableOrConstant.isVariable()) { + synonymMap.addSynonym(valueMove.variableOrConstant.asVariable(), valueMove.getLocation().asVariable()); + } } else { this.values.put(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index bfa9ee72c473..6dd5f08b2cfb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -71,6 +71,8 @@ public class RegAllocVerifier { */ protected ConflictResolver constantMaterializationConflictResolver; + protected VariableSynonymMap synonymMap; + public RegAllocVerifier(LIR lir, BlockMap> blockInstructions, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; this.registerAllocationConfig = registerAllocationConfig; @@ -82,6 +84,8 @@ public RegAllocVerifier(LIR lir, BlockMap> blockInstru this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); + + this.synonymMap = new VariableSynonymMap(); } /** @@ -131,7 +135,7 @@ public void calculateEntryBlocks() { } protected BlockVerifierState createNewBlockState(BasicBlock block) { - return new BlockVerifierState(block, registerAllocationConfig, constantMaterializationConflictResolver); + return new BlockVerifierState(block, registerAllocationConfig, constantMaterializationConflictResolver, synonymMap); } /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java new file mode 100644 index 000000000000..38ee78a0c8e8 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import jdk.graal.compiler.util.EconomicHashMap; + +import java.util.Map; + +public class VariableSynonymMap { + Map parent = new EconomicHashMap<>(); + Map rank = new EconomicHashMap<>(); + + public void addSynonym(RAVariable source, RAVariable target) { + union(source, target); + } + + public RAVariable find(RAVariable x) { + parent.putIfAbsent(x, x); + if (!parent.get(x).equals(x)) { + parent.put(x, find(parent.get(x))); + } + return parent.get(x); + } + + public void union(RAVariable a, RAVariable b) { + RAVariable rootA = find(a); + RAVariable rootB = find(b); + + if (rootA.equals(rootB)) { + return; + } + + int rankA = rank.getOrDefault(rootA, 0); + int rankB = rank.getOrDefault(rootB, 0); + + if (rankA < rankB) { + parent.put(rootA, rootB); + } else if (rankA > rankB) { + parent.put(rootB, rootA); + } else { + parent.put(rootB, rootA); + rank.put(rootA, rankA + 1); + } + } + + public boolean isSynonymOf(RAVariable source, RAVariable target) { + return find(source).equals(find(target)); + } +} From fbd27b110014774c6f173f68a54438497511afaf Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 25 Mar 2026 21:54:05 +0100 Subject: [PATCH 086/112] Refactor label variable resolver --- .../verifier/FromUsageResolverGlobal.java | 228 ++++++++++-------- 1 file changed, 130 insertions(+), 98 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index d9bb0b862434..2782959a3f80 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -26,7 +26,6 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.util.EconomicHashMap; @@ -42,19 +41,27 @@ /** * Resolve variables phi variables back to labels and jumps by find their first usage and handling * any reg allocator inserted moves back to the defining label. + * *

* Register allocator strips us of this information that is necessary for the verification. In order * to avoid modifying the existing allocators, we rather try to resolve this information from other * instructions. + *

+ * *

* Variables with only usage in jump instructions are marked as aliases and are resolved after their * successors. + *

+ * *

* If a variable has no usage, then no location is resolved and verification continues without * issues. + *

+ * *

* In the case the first usage of a label-defined variable is wrong, then JUMP instructions from * predecessors fail the verification - wrong register will be chosen. + *

*/ public class FromUsageResolverGlobal { /** @@ -72,16 +79,6 @@ public class FromUsageResolverGlobal { */ public Map labelMap; - /** - * Is this a label-defined variable? - */ - public Map defined; - - /** - * Was this label-defined variable reached? - */ - public Map reached; - /** * Mapping of operation to a set of variable for which this operation is a first usage. */ @@ -115,57 +112,40 @@ class AliasPair { /** * Block map of their usages objects. */ - public BlockMap blockUsageMap; + protected BlockMap blockUsageMap; /** * Set of blocks that have no successors. */ public Set> endBlocks; + /** + * Information about locations of variables found + * when traversing LIR, from first usage, handling + * all related moves up until the label instruction + * that defined the variable. + */ protected final class BlockUsage { - private final Set reached; - private final Map locations; + protected final Map locations; + protected boolean processed; - private BlockUsage() { - this.reached = new EconomicHashSet<>(); + protected BlockUsage() { this.locations = new EconomicHashMap<>(); + this.processed = false; } - private BlockUsage(BlockUsage blockDefs) { - this.reached = new EconomicHashSet<>(blockDefs.reached); - this.locations = new EconomicHashMap<>(blockDefs.locations); - } - - /** - * Two blocks meet, a successor merges into it's predecessor to pass in newly reached - * variable and locations. - * - * @param other Other block usage information - the successor - * @return Has the current block (predecessor) been changed? - */ - private boolean meetWith(BlockUsage other) { - int reachedBefore = reached.size(); - reached.addAll(other.reached); - - boolean changed = reachedBefore != reached.size(); - for (RAVariable variable : other.locations.keySet()) { - var defValue = other.locations.get(variable); - if (defValue == null || defValue.isIllegal()) { - continue; - } - - if (defValue.isIllegal() && locations.containsKey(variable)) { - continue; - } - - if (locations.containsKey(variable) && locations.get(variable) == defValue) { + protected BlockUsage(BlockUsage blockDefs, boolean processed) { + this.locations = new EconomicHashMap<>(); + for (var variable : blockDefs.locations.keySet()) { + var location = blockDefs.locations.get(variable); + if (location == null) { continue; } - locations.put(variable, defValue); + locations.put(variable, location); } - return changed; + this.processed = processed; } } @@ -174,8 +154,6 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b this.blockInstructions = blockInstructions; this.labelMap = new EconomicHashMap<>(); - this.defined = new EconomicHashMap<>(); - this.reached = new EconomicHashMap<>(); this.firstUsages = new EconomicHashMap<>(); this.initialLocations = new EconomicHashMap<>(); this.aliasMap = new EconomicHashMap<>(); @@ -201,51 +179,97 @@ public void resolvePhiFromUsage() { } while (!worklist.isEmpty()) { - var block = worklist.remove(); + if (labelMap.isEmpty()) { + break; // No need to process further + } - var usage = new BlockUsage(blockUsageMap.get(block)); - var instructions = blockInstructions.get(block); - for (var instruction : instructions.reversed()) { - switch (instruction) { - case RAVInstruction.LocationMove move -> handleMove(usage, move.from, move.to); - case RAVInstruction.Op op -> { - if (op.lirInstruction instanceof StandardOp.LabelOp) { - this.resolveLabel(usage, op, block); - continue; - } + processBlock(worklist); + } + } - if (firstUsages.containsKey(op)) { - var iterator = firstUsages.get(op).iterator(); - while (iterator.hasNext()) { - var variable = iterator.next(); - usage.locations.put(variable, initialLocations.get(variable)); - usage.reached.add(variable); - } - } + protected void processBlock(Queue> worklist) { + var block = worklist.remove(); + + var usage = blockUsageMap.get(block); + usage.processed = true; + + var instructions = blockInstructions.get(block); + for (var instruction : instructions.reversed()) { + switch (instruction) { + case RAVInstruction.LocationMove move -> handleMove(usage, move.from, move.to); + case RAVInstruction.Op op -> { + if (op.lirInstruction instanceof StandardOp.LabelOp) { + this.resolveLabel(usage, op, block); + continue; } - default -> { + + if (firstUsages.containsKey(op)) { + for (RAVariable variable : firstUsages.get(op)) { + usage.locations.put(variable, initialLocations.get(variable)); + } } } + default -> { + } } + } - for (int i = 0; i < block.getPredecessorCount(); i++) { - var pred = block.getPredecessorAt(i); + this.blockUsageMap.put(block, usage); + for (int i = 0; i < block.getPredecessorCount(); i++) { + var pred = block.getPredecessorAt(i); - if (this.blockUsageMap.get(pred) == null) { - this.blockUsageMap.put(pred, new BlockUsage(usage)); - } else { - var predReached = this.blockUsageMap.get(pred); - if (!predReached.meetWith(usage)) { + if (this.blockUsageMap.get(pred) == null) { + this.blockUsageMap.put(pred, new BlockUsage(usage, false)); + } else { + var predReached = this.blockUsageMap.get(pred); + if (!mergeInto(predReached, usage)) { + if (predReached.processed) { + continue; + } else if (worklist.contains(pred)) { continue; } + + // Not yet processed, but also not in a worklist + // this can happen when alias has been resolved and + // predecessor block needs to be processed again + // (the processed flag is set to false) } + } + + worklist.remove(pred); + worklist.add(pred); + } + } - worklist.remove(pred); - worklist.add(pred); + /** + * Two blocks meet, a successor merges into it's predecessor to pass in newly reached + * variable and locations. + * + * @param block The base block, where information is being merged to + * @param successor The successor block, where new information is coming from + * @return Has the current block (predecessor) been changed? + */ + protected boolean mergeInto(BlockUsage block, BlockUsage successor) { + boolean changed = false; + for (RAVariable variable : successor.locations.keySet()) { + if (!labelMap.containsKey(variable)) { + continue; // Do not push already resolved variables further + } + + var defValue = successor.locations.get(variable); + + if (block.locations.containsKey(variable) && block.locations.get(variable) == defValue) { + continue; } + + block.locations.put(variable, defValue); + changed = true; } + + return changed; } + /** * Initialize first usages for variables, top-down in-order to collect all necessary information * for the resolution. @@ -283,7 +307,6 @@ protected void initializeUsages() { } var variable = label.dests.orig[i].asVariable(); - defined.put(variable, true); labelMap.put(variable, label); } } @@ -310,7 +333,7 @@ protected void initializeUsages() { } var variable = jump.alive.orig[i].asVariable(); - if (defined.containsKey(variable) && !reached.containsKey(variable)) { + if (labelMap.containsKey(variable) && !initialLocations.containsKey(variable)) { // No usage found before this jump var succ = block.getSuccessorAt(0); var succLabel = (RAVInstruction.Op) blockInstructions.get(succ).getFirst(); @@ -331,6 +354,24 @@ protected void initializeUsages() { continue; } + if (block.isLoopHeader()) { + // Here we handle loops without any exit that might + // also need a resolution of label variables, but + // are not reachable from endBlocks set, so we + // add predecessors of such loops, that are part of the loop + // into the endBlocks set to process them. + var loop = block.getLoop(); + if (loop.getNaturalExits().isEmpty() && loop.getLoopExits().isEmpty()) { + var loopBlocks = loop.getBlocks(); + for (int i = 0; i < block.getPredecessorCount(); i++) { + var pred = block.getPredecessorAt(i); + if (loopBlocks.contains(pred)) { + endBlocks.add(pred); + } + } + } + } + for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); @@ -349,16 +390,12 @@ protected void initializeUsages() { */ protected void handleUsages(RAVInstruction.ValueArrayPair values, RAVInstruction.Op op, BasicBlock block) { for (var i = 0; i < values.count; i++) { - if (!values.orig[i].isVariable()) { + if (!values.orig[i].isVariable() || values.curr[i] == null) { continue; } var variable = values.orig[i].asVariable(); - if (defined.containsKey(variable) && !reached.containsKey(variable)) { - // Defined - variable comes from label - // Reached does not contain variable - there's no other first usage. - reached.put(variable, false); - + if (labelMap.containsKey(variable) && !initialLocations.containsKey(variable)) { if (!firstUsages.containsKey(op)) { firstUsages.put(op, new EconomicHashSet<>()); } @@ -392,11 +429,8 @@ protected void handleMove(BlockUsage usage, RAValue from, RAValue to) { for (var entry : usage.locations.entrySet()) { var variable = entry.getKey(); var location = entry.getValue(); - if (location == null || location.isIllegal()) { - continue; - } - if (location.equals(to) && usage.reached.contains(variable)) { + if (location.equals(to)) { updatedVariables.put(variable, from); } } @@ -419,19 +453,16 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc } var variable = label.dests.orig[i].asVariable(); - if (usage.locations.get(variable) == null) { - continue; // Not resolved yet - } - if (label.dests.curr[i] != null) { continue; // Already resolved } - var location = usage.locations.get(variable); - if (location == null || location.isIllegal()) { - GraalError.shouldNotReachHere("Location is " + location + " when resolving " + variable + " should not happen."); + if (!usage.locations.containsKey(variable)) { + continue; } + var location = usage.locations.get(variable); + label.dests.curr[i] = location; for (int j = 0; j < block.getPredecessorCount(); j++) { var pred = block.getPredecessorAt(j); @@ -451,11 +482,12 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc var aliasBlockUsage = blockUsageMap.get(aliasPair.block); aliasBlockUsage.locations.put(aliasPair.variable, location); - aliasBlockUsage.reached.add(aliasPair.variable); + aliasBlockUsage.processed = false; // Needs to be processed again } } - usage.locations.put(variable, null); + labelMap.remove(variable); + usage.locations.remove(variable); } } } From c28a22b51af71a909f92dd1c8481544ca90f5aa8 Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 25 Mar 2026 22:16:39 +0100 Subject: [PATCH 087/112] Skip register check for prealloc moves --- .../alloc/verifier/AllocationStateMap.java | 2 + .../alloc/verifier/BlockVerifierState.java | 46 ++++++++++++++++++- .../verifier/FromUsageResolverGlobal.java | 11 ++--- .../lir/alloc/verifier/RAVInstruction.java | 15 ++++++ .../lir/alloc/verifier/VerifierPrinter.java | 4 +- 5 files changed, 68 insertions(+), 10 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java index c39699c8a66f..188cc775dc7c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java @@ -41,10 +41,12 @@ * {@link ConflictedAllocationState conflicted} - set of Values that are supposed to be at same * location * + *

* Conflicts are resolved by assigning new {@link ValueAllocationState value} to same location. * Otherwise, they cannot be used. {@link ValueAllocationState Value} can store register, stack * slot, constant, but most importantly variables used before allocation. These are what we are * checking with the verification process. + *

*/ public class AllocationStateMap { protected BasicBlock block; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 532d9d359e47..2ded26d5e924 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -586,7 +586,11 @@ public void updateWithLocationMove(RAVInstruction.LocationMove move) { var state = this.values.get(move.from); - this.values.putClone(move.to, state); + if (move.validateRegisters) { + this.values.putClone(move.to, state); + } else { + this.values.putWithoutRegCheck(move.to, state.clone()); + } if (state instanceof ValueAllocationState valueAllocationState) { var movedValue = valueAllocationState.getRAValue(); @@ -610,6 +614,14 @@ protected void updateWithOp(RAVInstruction.Op op) { updateWithSafePoint(op); } + if (canCastOpToMove(op)) { + // Moves present before the allocation can also be treated + // same way the one inserted by the allocator + RAVInstruction.LocationMove locMove = castMove(op); + updateWithLocationMove(locMove); + return; + } + for (int i = 0; i < op.dests.count; i++) { if (op.dests.orig[i].isIllegal()) { continue; // Safe to ignore, when destination is illegal value, not when used. @@ -646,10 +658,40 @@ protected void updateWithOp(RAVInstruction.Op op) { // We cannot believe the contents of registers used as temp, thus we need to reset. RAValue location = op.temp.curr[i]; - this.values.put(location, UnknownAllocationState.INSTANCE); + + if (op.temp.orig[i].equals(location)) { + this.values.putWithoutRegCheck(location, UnknownAllocationState.INSTANCE); + } else { + this.values.put(location, UnknownAllocationState.INSTANCE); + } } } + /** + * Moves not inserted by the register allocator were previously treated as generic Operations, + * but some of them can be cast back to a move to increase the accuracy of the verification + * process. + * + *

+ * All moves where the destination is the same location before and after the allocation, or + * vstack/stack combination can be cast to a move. + *

+ * + * @param op the operation being cast + * @return true, if op can be cast to a move + */ + protected boolean canCastOpToMove(RAVInstruction.Op op) { + if (!op.lirInstruction.isMoveOp() || op.dests.count != 1 || op.uses.count != 1) { + return false; + } + + return op.dests.curr[0].equals(op.dests.orig[0]) || (ValueUtil.isStackSlot(op.dests.curr[0].getValue()) && LIRValueUtil.isVirtualStackSlot(op.dests.orig[0].getValue())); + } + + protected RAVInstruction.LocationMove castMove(RAVInstruction.Op op) { + return new RAVInstruction.LocationMove(op.lirInstruction, op.uses.curr[0].getValue(), op.dests.curr[0].getValue(), false); + } + /** * Update block state with a safe point list of live references deemed by the GC, any other * references not included in said list are to be set as unknown so there's no freed pointer diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 2782959a3f80..ebbeb1764970 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -120,10 +120,8 @@ class AliasPair { public Set> endBlocks; /** - * Information about locations of variables found - * when traversing LIR, from first usage, handling - * all related moves up until the label instruction - * that defined the variable. + * Information about locations of variables found when traversing LIR, from first usage, + * handling all related moves up until the label instruction that defined the variable. */ protected final class BlockUsage { protected final Map locations; @@ -242,8 +240,8 @@ protected void processBlock(Queue> worklist) { } /** - * Two blocks meet, a successor merges into it's predecessor to pass in newly reached - * variable and locations. + * Two blocks meet, a successor merges into it's predecessor to pass in newly reached variable + * and locations. * * @param block The base block, where information is being merged to * @param successor The successor block, where new information is coming from @@ -269,7 +267,6 @@ protected boolean mergeInto(BlockUsage block, BlockUsage successor) { return changed; } - /** * Initialize first usages for variables, top-down in-order to collect all necessary information * for the resolution. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 9bdd1e9eda62..f341306a5592 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -362,11 +362,26 @@ public String toString() { public static class LocationMove extends Base { public RAValue from; public RAValue to; + public boolean validateRegisters; public LocationMove(LIRInstruction instr, Value from, Value to) { + this(instr, from, to, true); + } + + /** + * Create a location move instance. + * + * @param instr Underlying LIR instruction + * @param from Source value + * @param to Destination value + * @param validateRegisters If checking, if register can be allocated to, should be done. + * This is false for moves that are not inserted or changed by the allocator. + */ + public LocationMove(LIRInstruction instr, Value from, Value to, boolean validateRegisters) { super(instr); this.from = RAValue.create(from); this.to = RAValue.create(to); + this.validateRegisters = validateRegisters; } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index 57880403b5b3..7b5fd13c9b13 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -32,6 +32,8 @@ import java.util.List; public class VerifierPrinter { + public static int PADDING = 4; + /** * Print human-readable representation of the Verifier IR to an output stream. * @@ -81,7 +83,7 @@ public static void printAligned(PrintStream out, LIR lir, BlockMap Date: Thu, 26 Mar 2026 14:03:02 +0100 Subject: [PATCH 088/112] Handle callee-saved values from virtual moves --- .../alloc/verifier/BlockVerifierState.java | 57 +++++++- .../lir/alloc/verifier/CalleeSaveMap.java | 128 ++++++++++++++++++ ...leeSavedRegisterNotRetrievedException.java | 44 ++++++ .../lir/alloc/verifier/RegAllocVerifier.java | 13 +- 4 files changed, 233 insertions(+), 9 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 2ded26d5e924..ec0b159af1eb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -68,11 +68,15 @@ public class BlockVerifierState { protected VariableSynonymMap synonymMap; - public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver, VariableSynonymMap synonymMap) { + protected CalleeSaveMap calleeSaveMap; + + public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver, VariableSynonymMap synonymMap, + CalleeSaveMap calleeSaveMap) { this.values = new AllocationStateMap(block, registerAllocationConfig); this.registerAllocationConfig = registerAllocationConfig; this.conflictConstantResolver = constantConflictResolver; this.synonymMap = synonymMap; + this.calleeSaveMap = calleeSaveMap; this.block = block; } @@ -81,6 +85,7 @@ protected BlockVerifierState(BasicBlock block, BlockVerifierState other) { this.conflictConstantResolver = other.conflictConstantResolver; this.values = new AllocationStateMap(block, other.values); this.synonymMap = other.synonymMap; + this.calleeSaveMap = other.calleeSaveMap; this.block = block; } @@ -665,6 +670,10 @@ protected void updateWithOp(RAVInstruction.Op op) { this.values.put(location, UnknownAllocationState.INSTANCE); } } + + if (op.isLabel() && block.getId() == 0) { + updateCalleeSavedRegisters(); + } } /** @@ -746,10 +755,25 @@ protected void updateCalleeSavedRegisters() { } for (var reg : registers) { - var regValue = RARegister.create(reg.asValue()); + var regValue = calleeSaveMap.createCalleeSavedRegister(reg.asValue()); + + var presentState = values.get(regValue); + if (presentState instanceof ValueAllocationState valueAllocationState) { + // Keep the old value from the label, but save it for check at exit point. + if (!valueAllocationState.getRAValue().equals(regValue)) { + // If the symbol is the same register, then override it with CalleeSaveRegister + calleeSaveMap.addValue(regValue, valueAllocationState.getRAValue()); + continue; + } + + // Keep the kind here! + var lirRegValue = ValueUtil.asRegisterValue(valueAllocationState.getValue()); + regValue = calleeSaveMap.createCalleeSavedRegister(lirRegValue); + } // Save same registers as symbol, and later check if it was retrieved - this.values.putWithoutRegCheck(regValue, new ValueAllocationState(regValue, null, block)); + var state = new ValueAllocationState(regValue, null, block); + this.values.putWithoutRegCheck(regValue, state); } } @@ -760,22 +784,25 @@ protected void updateCalleeSavedRegisters() { * @throws RAVException when callee saved register was not recovered */ protected void checkCalleeSavedRegisters() { - var registers = this.registerAllocationConfig.getRegisterConfig().getCalleeSaveRegisters(); + var registers = this.calleeSaveMap.getCalleeSaveRegisters(); if (registers == null) { return; } for (var reg : registers) { - var regValue = RARegister.create(reg.asValue()); + var regValue = (RARegister) RARegister.create(reg.asValue()); var state = this.values.get(regValue); if (state instanceof ValueAllocationState valueAllocationState) { - if (valueAllocationState.getRAValue().equals(regValue)) { + var stateValue = valueAllocationState.getRAValue(); + var calleeSavedValue = calleeSaveMap.getCalleeSavedValue(regValue); + if (stateValue.equals(calleeSavedValue) && stateValue.getLIRKind().equals(calleeSavedValue.getLIRKind())) { // Same symbol as register means the value was retrieved safely + // Kinds also need to match continue; } } - throw new RAVException("Callee saved register " + regValue + " not recovered."); + throw new CalleeSavedRegisterNotRetrievedException(regValue, block); } } @@ -802,6 +829,22 @@ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove) { if (valueMove.variableOrConstant.isVariable()) { synonymMap.addSynonym(valueMove.variableOrConstant.asVariable(), valueMove.getLocation().asVariable()); } + } else if (location.isRegister() && valueMove.variableOrConstant.isVariable()) { + var regLoc = location.asRegister(); + + var state = this.values.get(regLoc); + if (state instanceof ValueAllocationState valueAllocationState) { + var value = valueAllocationState.getRAValue(); + if (value instanceof CalleeSaveMap.CalleeSavedRegister) { + // Virtual move in form r1 = VIRTMOVE v1, assigns variable + // v1 to callee saved register, this needs to be saved + // to properly check that callee saved value is retrieved + // at exit point. + calleeSaveMap.addValue(regLoc, valueMove.variableOrConstant.asVariable()); + } + } + + this.values.putWithoutRegCheck(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); } else { this.values.put(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java new file mode 100644 index 000000000000..db0151f9c0d3 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import java.util.List; +import java.util.Map; + +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterConfig; +import jdk.vm.ci.code.RegisterValue; + +/** + * Make sure callee-saved register values are retrieved at exit block. + * + *

+ * Virtual moves assigning to same registers need to also be handled, because both values could be + * retrieved. + *

+ */ +public class CalleeSaveMap { + protected RegisterConfig registerConfig; + protected List calleeSaveRegisters; + + protected Map virtualValues; + + public CalleeSaveMap(RegisterConfig registerConfig) { + this.registerConfig = registerConfig; + calleeSaveRegisters = registerConfig.getCalleeSaveRegisters(); + virtualValues = new EconomicHashMap<>(); + } + + public class CalleeSavedRegister extends RARegister { + protected CalleeSavedRegister(RegisterValue registerValue) { + super(registerValue); + } + + @Override + public String toString() { + return "CalleeSaved " + super.toString(); + } + } + + /** + * Create a callee saved register, this is signalling that this value needs to be retrieved on + * exit point, not just same register value. + * + * @param registerValue Callee saved register + * @return Instance of callee saved register + */ + public CalleeSavedRegister createCalleeSavedRegister(RegisterValue registerValue) { + var calleeSavedRegister = new CalleeSavedRegister(registerValue); + virtualValues.put(calleeSavedRegister, calleeSavedRegister); + return calleeSavedRegister; + } + + /** + * Add a variable from virtual move that is assigned to a callee saved register and can also be + * retrieved on exit. + * + * @param register Callee saved register + * @param value New callee saved value + */ + public void addValue(RARegister register, RAValue value) { + if (!isCalleeSaveRegister(register)) { + return; + } + + if (virtualValues.get(register) instanceof CalleeSavedRegister) { + virtualValues.put(register, value); + } + } + + /** + * Is callee saved value? + * + * @param register Callee saved register + * @return Get callee saved value for this register + */ + public RAValue getCalleeSavedValue(RARegister register) { + return virtualValues.get(register); + } + + /** + * Is register callee-saved? + * + * @param register Register + * @return true, if register is callee saved + */ + public boolean isCalleeSaveRegister(RARegister register) { + if (calleeSaveRegisters == null) { + return false; + } + + return calleeSaveRegisters.contains(register.getRegister()); + } + + /** + * Get list of callee saved registers. + * + * @return callee saved registers + */ + public List getCalleeSaveRegisters() { + return calleeSaveRegisters; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java new file mode 100644 index 000000000000..4f1eee671dc7 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; + +/** + * Callee-saved register was not retrieved on exit block. + */ +@SuppressWarnings("serial") +public class CalleeSavedRegisterNotRetrievedException extends RAVException { + public RARegister register; + public BasicBlock block; + + public CalleeSavedRegisterNotRetrievedException(RARegister register, BasicBlock block) { + super(getErrorMessage(register, block)); + } + + public static String getErrorMessage(RARegister register, BasicBlock block) { + return "Callee saved register " + register + " not retrieved on exit " + block; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 6dd5f08b2cfb..d7a496f5a1a5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -73,6 +73,11 @@ public class RegAllocVerifier { protected VariableSynonymMap synonymMap; + /** + * Track callee saved values from start block to exit blocks. + */ + protected CalleeSaveMap calleeSaveMap; + public RegAllocVerifier(LIR lir, BlockMap> blockInstructions, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; this.registerAllocationConfig = registerAllocationConfig; @@ -86,6 +91,7 @@ public RegAllocVerifier(LIR lir, BlockMap> blockInstru this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); this.synonymMap = new VariableSynonymMap(); + this.calleeSaveMap = new CalleeSaveMap(registerAllocationConfig.getRegisterConfig()); } /** @@ -100,7 +106,6 @@ public void calculateEntryBlocks() { var startBlock = this.lir.getControlFlowGraph().getStartBlock(); var startBlockState = createNewBlockState(startBlock); - startBlockState.updateCalleeSavedRegisters(); this.blockEntryStates.put(startBlock, startBlockState); worklist.add(startBlock); @@ -135,7 +140,7 @@ public void calculateEntryBlocks() { } protected BlockVerifierState createNewBlockState(BasicBlock block) { - return new BlockVerifierState(block, registerAllocationConfig, constantMaterializationConflictResolver, synonymMap); + return new BlockVerifierState(block, registerAllocationConfig, constantMaterializationConflictResolver, synonymMap, calleeSaveMap); } /** @@ -200,12 +205,16 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { * variable locations back, calculating entry state for every block so that at the end we can * verify inputs of instructions match variables present before allocation. * + *

* The issues we are looking to catch are mostly about making sure that order of spills, reloads * and moves is correct and that used location after stores the symbol that is supposed to be * there. + *

* + *

* We also make sure that kinds are still matching, operand flags aren't violated, alive * location not being used as temp or output of same instruction. + *

*/ public void run() { this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); From 1ff83e7d708958beb3bbbe966dc48018d6fb7599 Mon Sep 17 00:00:00 2001 From: danocmx Date: Thu, 26 Mar 2026 14:03:54 +0100 Subject: [PATCH 089/112] Check for kinds in value moves --- .../alloc/verifier/BlockVerifierState.java | 19 +++++++++++++++++-- .../lir/alloc/verifier/RAVInstruction.java | 19 ++++++++++++++++++- .../alloc/verifier/RegAllocVerifierPhase.java | 2 +- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index ec0b159af1eb..7f1570527964 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -315,7 +315,9 @@ public void check(RAVInstruction.Base instruction) { checkReferences(op); } } else if (instruction instanceof RAVInstruction.LocationMove move) { - checkMoveKinds(move); + checkLocationMoveKinds(move); + } else if (instruction instanceof RAVInstruction.ValueMove move) { + checkValueMoveKinds(move); } } @@ -365,7 +367,7 @@ protected void checkReferences(RAVInstruction.Op op) { * * @param move Move between locations, inserted by register allocator */ - protected void checkMoveKinds(RAVInstruction.LocationMove move) { + protected void checkLocationMoveKinds(RAVInstruction.LocationMove move) { AllocationState state = this.values.get(move.from); if (state instanceof ValueAllocationState valueAllocationState) { RAValue movedValue = valueAllocationState.getRAValue(); @@ -381,6 +383,19 @@ protected void checkMoveKinds(RAVInstruction.LocationMove move) { } } + protected void checkValueMoveKinds(RAVInstruction.ValueMove move) { + if (move instanceof RAVInstruction.VirtualLocationMove) { + // v28|DWORD = MOVE input: rax|BYTE moveKind: DWORD + // this type of instruction that is stripped from final + // LIR is not checked for kinds. + return; + } + + if (!kindsEqual(move.getLocation(), move.variableOrConstant)) { + throw new KindsMismatchException(move.lirInstruction, block, move.getLocation(), move.variableOrConstant, false); + } + } + /** * Does this move change the ValueKind? This happens for when derived reference gets changed to * a normal reference, we make sure that the underlying platform kind is equal, and then allow diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index f341306a5592..1ab3194997c0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -497,7 +497,7 @@ public ValueMove(LIRInstruction instr, Value variableOrConstant, Value location) @Override public String toString() { - return getLocation().toString() + " = VIRTMOVE " + variableOrConstant.toString(); + return getLocation().toString() + " = VALUEMOVE " + variableOrConstant.toString(); } public void setLocation(RAValue location) { @@ -511,4 +511,21 @@ public RAValue getLocation() { return location; } } + + /** + * Virtual move in from: v28|DWORD = MOVE input: rax|BYTE moveKind: DWORD, + * where the destination is a variable. We flip the relation so that + * the input register actually stores said symbol/variable, which keeps + * necessary verification information present. + */ + public static class VirtualLocationMove extends ValueMove { + public VirtualLocationMove(LIRInstruction instr, Value variableOrConstant, Value location) { + super(instr, variableOrConstant, location); + } + + @Override + public String toString() { + return getLocation().toString() + " = VIRTMOVE " + variableOrConstant.toString(); + } + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 30e1478b27e3..0082e7faaeb3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -192,7 +192,7 @@ protected Map saveInstructionsPreAlloc(LIR var location = valueMov.getInput(); var variable = LIRValueUtil.asVariable(valueMov.getResult()); - var virtualMove = new RAVInstruction.ValueMove(instruction, variable, location); + var virtualMove = new RAVInstruction.VirtualLocationMove(instruction, variable, location); previousInstr.addVirtualMove(virtualMove); // No need to store virtual move here, it is stored into previous instruction. From 67b27510f70702f69ab7b1770685cd2eb5ac8995 Mon Sep 17 00:00:00 2001 From: danocmx Date: Thu, 26 Mar 2026 14:04:34 +0100 Subject: [PATCH 090/112] Fix resolver merge state base --- .../lir/alloc/verifier/FromUsageResolverGlobal.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index ebbeb1764970..d8ca223a9c4b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -188,8 +188,9 @@ public void resolvePhiFromUsage() { protected void processBlock(Queue> worklist) { var block = worklist.remove(); - var usage = blockUsageMap.get(block); - usage.processed = true; + var exitUsage = blockUsageMap.get(block); + exitUsage.processed = true; + var usage = new BlockUsage(exitUsage, true); var instructions = blockInstructions.get(block); for (var instruction : instructions.reversed()) { @@ -212,7 +213,6 @@ protected void processBlock(Queue> worklist) { } } - this.blockUsageMap.put(block, usage); for (int i = 0; i < block.getPredecessorCount(); i++) { var pred = block.getPredecessorAt(i); From 6e159b80d83d4e3ce09fb77288debd3bf2b1adc9 Mon Sep 17 00:00:00 2001 From: danocmx Date: Thu, 26 Mar 2026 15:37:35 +0100 Subject: [PATCH 091/112] Change synonym map to a conflict resolver --- .../alloc/verifier/BlockVerifierState.java | 36 ++++++---- .../lir/alloc/verifier/RegAllocVerifier.java | 8 +++ .../alloc/verifier/VariableSynonymMap.java | 71 +++++++++++++++++-- 3 files changed, 95 insertions(+), 20 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 7f1570527964..7ba2dc33f278 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -206,6 +206,15 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } } + if (orig.isVariable()) { + var variable = orig.asVariable(); + var resolvedState = this.synonymMap.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); + if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { + this.values.put(curr, resolvedState); + return; + } + } + throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); } @@ -220,8 +229,13 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } } - if (orig.isVariable() && valAllocState.value.isVariable() && synonymMap.isSynonymOf(orig.asVariable(), valAllocState.value.asVariable())) { - return; // Resolved! + if (orig.isVariable()) { + var variable = orig.asVariable(); + var resolvedState = this.synonymMap.resolveValueState(variable, valAllocState, curr); + if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { + this.values.put(curr, resolvedState); + return; + } } throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); @@ -829,21 +843,13 @@ protected void checkCalleeSavedRegisters() { * @param valueMove move we update state from */ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove) { - if (valueMove.getLocation().isVariable()) { - // Whenever there is a move between two variables, - // we need to change every location containing the old variable (rhs - source) - // to the new variable (lhs - destination) + var location = valueMove.getLocation(); + if (location.isVariable()) { + // Moves of this form: // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD - // Move before allocation + // are handled by VariableSynonymMap. // TestCase: BoxingTest.boxBoolean - var locations = this.values.getValueLocations(valueMove.variableOrConstant); - for (var location : locations) { - this.values.put(location, new ValueAllocationState(valueMove.getLocation(), valueMove, block)); - } - - if (valueMove.variableOrConstant.isVariable()) { - synonymMap.addSynonym(valueMove.variableOrConstant.asVariable(), valueMove.getLocation().asVariable()); - } + return; } else if (location.isRegister() && valueMove.variableOrConstant.isVariable()) { var regLoc = location.asRegister(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index d7a496f5a1a5..e3327f61050f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -71,6 +71,11 @@ public class RegAllocVerifier { */ protected ConflictResolver constantMaterializationConflictResolver; + /** + * Conflict resolver for variable synonyms, some virtual + * moves can be in form vx = MOVE vy, and so variables are + * interchangeable. + */ protected VariableSynonymMap synonymMap; /** @@ -99,7 +104,9 @@ public RegAllocVerifier(LIR lir, BlockMap> blockInstru * blocks that are its predecessors, we get after reached a fixed point state, where no entry * state is changed. * + *

* This is necessary to verify instruction inputs correctly. + *

*/ public void calculateEntryBlocks() { Queue> worklist = new ArrayDeque<>(); @@ -218,6 +225,7 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { */ public void run() { this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); + this.synonymMap.prepare(lir, blockInstructions); this.fromUsageResolverGlobal.resolvePhiFromUsage(); this.calculateEntryBlocks(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index 38ee78a0c8e8..58f85a853fc5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -24,19 +24,27 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.util.EconomicHashMap; +import java.util.List; import java.util.Map; -public class VariableSynonymMap { +/** + * Union-find data structure for synonyms between + * variables created by virtual moves. + */ +public class VariableSynonymMap implements ConflictResolver { Map parent = new EconomicHashMap<>(); Map rank = new EconomicHashMap<>(); - public void addSynonym(RAVariable source, RAVariable target) { + protected void addSynonym(RAVariable source, RAVariable target) { union(source, target); } - public RAVariable find(RAVariable x) { + protected RAVariable find(RAVariable x) { parent.putIfAbsent(x, x); if (!parent.get(x).equals(x)) { parent.put(x, find(parent.get(x))); @@ -44,7 +52,7 @@ public RAVariable find(RAVariable x) { return parent.get(x); } - public void union(RAVariable a, RAVariable b) { + protected void union(RAVariable a, RAVariable b) { RAVariable rootA = find(a); RAVariable rootB = find(b); @@ -65,7 +73,60 @@ public void union(RAVariable a, RAVariable b) { } } - public boolean isSynonymOf(RAVariable source, RAVariable target) { + protected boolean isSynonymOf(RAVariable source, RAVariable target) { return find(source).equals(find(target)); } + + @Override + public void prepare(LIR lir, BlockMap> blockInstructions) { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructions = blockInstructions.get(block); + + for (var instruction : instructions) { + this.prepareFromInstr(instruction, block); + } + } + } + + public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { + if (instruction instanceof RAVInstruction.ValueMove move) { + if (!move.variableOrConstant.isVariable() || !move.getLocation().isVariable()) { + return; + } + + this.addSynonym(move.variableOrConstant.asVariable(), move.getLocation().asVariable()); + } + } + + @Override + public ValueAllocationState resolveValueState(RAVariable target, ValueAllocationState valueState, RAValue location) { + var stateValue = valueState.getRAValue(); + if (!stateValue.isVariable()) { + return null; + } + + if (!isSynonymOf(target, stateValue.asVariable())) { + return null; + } + + return new ValueAllocationState(target, null, null); + } + + @Override + public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { + var confStates = conflictedState.getConflictedStates(); + for (var valueState : confStates) { + var stateValue = valueState.getRAValue(); + if (!stateValue.isVariable()) { + return null; + } + + if (!isSynonymOf(target, stateValue.asVariable())) { + return null; + } + + } + return new ValueAllocationState(target, null, null); + } } From eb80d21058041b0703a00e0d65536d5351fcf01b Mon Sep 17 00:00:00 2001 From: danocmx Date: Thu, 26 Mar 2026 16:14:09 +0100 Subject: [PATCH 092/112] Run formatter --- .../compiler/lir/alloc/verifier/RAVInstruction.java | 7 +++---- .../compiler/lir/alloc/verifier/RegAllocVerifier.java | 5 ++--- .../lir/alloc/verifier/VariableSynonymMap.java | 11 +++++------ 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 1ab3194997c0..3c9479307751 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -513,10 +513,9 @@ public RAValue getLocation() { } /** - * Virtual move in from: v28|DWORD = MOVE input: rax|BYTE moveKind: DWORD, - * where the destination is a variable. We flip the relation so that - * the input register actually stores said symbol/variable, which keeps - * necessary verification information present. + * Virtual move in from: v28|DWORD = MOVE input: rax|BYTE moveKind: DWORD, where the destination + * is a variable. We flip the relation so that the input register actually stores said + * symbol/variable, which keeps necessary verification information present. */ public static class VirtualLocationMove extends ValueMove { public VirtualLocationMove(LIRInstruction instr, Value variableOrConstant, Value location) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index e3327f61050f..46726467d969 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -72,9 +72,8 @@ public class RegAllocVerifier { protected ConflictResolver constantMaterializationConflictResolver; /** - * Conflict resolver for variable synonyms, some virtual - * moves can be in form vx = MOVE vy, and so variables are - * interchangeable. + * Conflict resolver for variable synonyms, some virtual moves can be in form vx = MOVE vy, and + * so variables are interchangeable. */ protected VariableSynonymMap synonymMap; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index 58f85a853fc5..a4e4dfe66267 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -33,8 +33,7 @@ import java.util.Map; /** - * Union-find data structure for synonyms between - * variables created by virtual moves. + * Union-find data structure for synonyms between variables created by virtual moves. */ public class VariableSynonymMap implements ConflictResolver { Map parent = new EconomicHashMap<>(); @@ -91,11 +90,11 @@ public void prepare(LIR lir, BlockMap> blockInstructio public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { if (instruction instanceof RAVInstruction.ValueMove move) { - if (!move.variableOrConstant.isVariable() || !move.getLocation().isVariable()) { - return; - } + if (!move.variableOrConstant.isVariable() || !move.getLocation().isVariable()) { + return; + } - this.addSynonym(move.variableOrConstant.asVariable(), move.getLocation().asVariable()); + this.addSynonym(move.variableOrConstant.asVariable(), move.getLocation().asVariable()); } } From 7d9daf55f9306b2f1013082dab0f6f138e5b56b3 Mon Sep 17 00:00:00 2001 From: danocmx Date: Thu, 26 Mar 2026 21:38:04 +0100 Subject: [PATCH 093/112] Remove unused block parameter --- .../compiler/lir/alloc/verifier/VariableSynonymMap.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index a4e4dfe66267..696d9da1314a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -24,7 +24,6 @@ */ package jdk.graal.compiler.lir.alloc.verifier; -import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.util.EconomicHashMap; @@ -83,12 +82,12 @@ public void prepare(LIR lir, BlockMap> blockInstructio var instructions = blockInstructions.get(block); for (var instruction : instructions) { - this.prepareFromInstr(instruction, block); + this.prepareFromInstr(instruction); } } } - public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { + public void prepareFromInstr(RAVInstruction.Base instruction) { if (instruction instanceof RAVInstruction.ValueMove move) { if (!move.variableOrConstant.isVariable() || !move.getLocation().isVariable()) { return; From 1eb15c7396634e3f0ef86dcf41dd8bd59fec48ff Mon Sep 17 00:00:00 2001 From: danocmx Date: Fri, 27 Mar 2026 19:41:30 +0100 Subject: [PATCH 094/112] Handle variable synonyms in constant materialization --- .../alloc/verifier/BlockVerifierState.java | 3 +- ...nstantMaterializationConflictResolver.java | 28 +++++++++++++++---- .../lir/alloc/verifier/RegAllocVerifier.java | 4 +-- .../alloc/verifier/RegAllocVerifierPhase.java | 10 +++++++ .../alloc/verifier/VariableSynonymMap.java | 2 +- 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 7ba2dc33f278..67f02ba4bba0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -70,7 +70,8 @@ public class BlockVerifierState { protected CalleeSaveMap calleeSaveMap; - public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, ConflictResolver constantConflictResolver, VariableSynonymMap synonymMap, + public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, + ConflictResolver constantConflictResolver, VariableSynonymMap synonymMap, CalleeSaveMap calleeSaveMap) { this.values = new AllocationStateMap(block, registerAllocationConfig); this.registerAllocationConfig = registerAllocationConfig; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index f51466293f13..df662791be87 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -47,10 +47,12 @@ public class ConstantMaterializationConflictResolver implements ConflictResolver { protected Map constantVariableMap; protected Set canRematerializeToStack; + protected VariableSynonymMap variableSynonymMap; - public ConstantMaterializationConflictResolver() { + public ConstantMaterializationConflictResolver(VariableSynonymMap variableSynonymMap) { this.constantVariableMap = new EconomicHashMap<>(); this.canRematerializeToStack = new EconomicHashSet<>(); + this.variableSynonymMap = variableSynonymMap; } /** @@ -106,6 +108,18 @@ public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock bloc */ @Override public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { + if (!this.constantVariableMap.containsKey(target)) { + var synonym = variableSynonymMap.find(target); + if (this.constantVariableMap.containsKey(synonym) && !synonym.equals(target)) { + var resolvedState = resolveConflictedState(synonym, conflictedState, location); + if (resolvedState != null) { + return new ValueAllocationState(target, resolvedState.getSource(), resolvedState.getBlock()); + } + } + + return null; + } + var confStates = conflictedState.getConflictedStates(); RAVariable variable = null; @@ -132,10 +146,6 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted return null; } - if (!this.constantVariableMap.containsKey(variable)) { - return null; - } - if (!this.constantVariableMap.get(variable).equals(constantState.getValue())) { return null; } @@ -158,6 +168,14 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted @Override public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocationState valueState, RAValue location) { if (!this.constantVariableMap.containsKey(variable)) { + var synonym = variableSynonymMap.find(variable); + if (this.constantVariableMap.containsKey(synonym) && !synonym.equals(variable)) { + var resolvedState = resolveValueState(synonym, valueState, location); + if (resolvedState != null) { + return new ValueAllocationState(variable, resolvedState.getSource(), resolvedState.getBlock()); + } + } + return null; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 46726467d969..6a7720f8e964 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -92,9 +92,9 @@ public RegAllocVerifier(LIR lir, BlockMap> blockInstru this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); - this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(); - this.synonymMap = new VariableSynonymMap(); + this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(this.synonymMap); + this.calleeSaveMap = new CalleeSaveMap(registerAllocationConfig.getRegisterConfig()); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 0082e7faaeb3..ca2041091f1e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -95,6 +95,16 @@ public RegAllocVerifierPhase(RegisterAllocationPhase allocator, LIRPhase Date: Fri, 27 Mar 2026 21:25:44 +0100 Subject: [PATCH 095/112] Add sources to synonym variable states --- .../alloc/verifier/VariableSynonymMap.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index c1c8a63efb35..e7aa37493be5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.util.EconomicHashMap; @@ -35,8 +36,13 @@ * Union-find data structure for synonyms between variables created by virtual moves. */ public class VariableSynonymMap implements ConflictResolver { - Map parent = new EconomicHashMap<>(); - Map rank = new EconomicHashMap<>(); + protected Map parent; + protected Map rank; + + protected VariableSynonymMap() { + parent = new EconomicHashMap<>(); + rank = new EconomicHashMap<>(); + } protected void addSynonym(RAVariable source, RAVariable target) { union(source, target); @@ -108,11 +114,14 @@ public ValueAllocationState resolveValueState(RAVariable target, ValueAllocation return null; } - return new ValueAllocationState(target, null, null); + return new ValueAllocationState(target, valueState.source, valueState.block); } @Override public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { + RAVInstruction.Base source = null; + BasicBlock block = null; + var confStates = conflictedState.getConflictedStates(); for (var valueState : confStates) { var stateValue = valueState.getRAValue(); @@ -124,7 +133,11 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted return null; } + // Currently take any source, but maybe its better to track the original variable + source = valueState.source; + block = valueState.block; } - return new ValueAllocationState(target, null, null); + + return new ValueAllocationState(target, source, block); } } From 570de058393a090806bf3745a817efda7ece8ff1 Mon Sep 17 00:00:00 2001 From: danocmx Date: Fri, 27 Mar 2026 22:03:42 +0100 Subject: [PATCH 096/112] Refactor conflict resolver logic in checkOperand --- .../alloc/verifier/BlockVerifierState.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 67f02ba4bba0..2fc4875b19ae 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -200,16 +200,13 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { if (state.isConflicted()) { if (orig.isVariable()) { var variable = orig.asVariable(); - var resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); - if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { - this.values.put(curr, resolvedState); - return; + var confState = (ConflictedAllocationState) state; + + ValueAllocationState resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, confState, curr); + if (resolvedState == null) { + resolvedState = this.synonymMap.resolveConflictedState(variable, confState, curr); } - } - if (orig.isVariable()) { - var variable = orig.asVariable(); - var resolvedState = this.synonymMap.resolveConflictedState(variable, (ConflictedAllocationState) state, curr); if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { this.values.put(curr, resolvedState); return; @@ -221,18 +218,16 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { if (state instanceof ValueAllocationState valAllocState) { if (!valAllocState.value.equals(orig)) { - if (LIRValueUtil.isConstantValue(valAllocState.value.getValue()) && orig.isVariable()) { + if (orig.isVariable()) { var variable = orig.asVariable(); - var resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); - if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { - this.values.put(curr, resolvedState); - return; + + ValueAllocationState resolvedState = null; + if (LIRValueUtil.isConstantValue(valAllocState.value.getValue())) { + resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); + } else if (valAllocState.getRAValue().isVariable()) { + resolvedState = this.synonymMap.resolveValueState(variable, valAllocState, curr); } - } - if (orig.isVariable()) { - var variable = orig.asVariable(); - var resolvedState = this.synonymMap.resolveValueState(variable, valAllocState, curr); if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { this.values.put(curr, resolvedState); return; From 301229820d41e20a87ac096037ce5c764f1b3b1c Mon Sep 17 00:00:00 2001 From: danocmx Date: Fri, 27 Mar 2026 22:11:57 +0100 Subject: [PATCH 097/112] Run formatter --- .../graal/compiler/lir/alloc/verifier/VariableSynonymMap.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index e7aa37493be5..1d1905ff1291 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -40,8 +40,8 @@ public class VariableSynonymMap implements ConflictResolver { protected Map rank; protected VariableSynonymMap() { - parent = new EconomicHashMap<>(); - rank = new EconomicHashMap<>(); + parent = new EconomicHashMap<>(); + rank = new EconomicHashMap<>(); } protected void addSynonym(RAVariable source, RAVariable target) { From 68acbacade9c3eb79de6ee82cfa7a1e9df8e5a33 Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 1 Apr 2026 22:59:56 +0200 Subject: [PATCH 098/112] Improve debug file logging --- .../AliveConstraintViolationException.java | 15 +- .../alloc/verifier/AllocationStateMap.java | 14 +- .../alloc/verifier/BlockVerifierState.java | 50 +-- ...leeSavedRegisterNotRetrievedException.java | 13 +- .../verifier/ConflictedAllocationState.java | 22 +- ...nstantMaterializationConflictResolver.java | 4 +- ...onstantRematerializedToStackException.java | 43 +++ .../InvalidRegisterUsedException.java | 5 +- .../verifier/KindsMismatchException.java | 15 +- ...ror.java => MissingLocationException.java} | 13 +- .../verifier/MissingReferenceException.java | 12 +- .../OperandFlagMismatchException.java | 9 +- .../lir/alloc/verifier/RAVException.java | 29 +- .../RAVFailedVerificationException.java | 8 +- .../lir/alloc/verifier/RAVInstruction.java | 9 +- .../lir/alloc/verifier/RegAllocVerifier.java | 7 +- .../alloc/verifier/RegAllocVerifierPhase.java | 20 +- .../verifier/UnknownAllocationState.java | 4 +- .../alloc/verifier/ValueAllocationState.java | 16 +- .../verifier/ValueNotInRegisterException.java | 41 +- .../lir/alloc/verifier/VerifierPrinter.java | 352 ++++++++++++++---- 21 files changed, 487 insertions(+), 214 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantRematerializedToStackException.java rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{MissingLocationError.java => MissingLocationException.java} (74%) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index d36b8ed5719e..f6848f20fe55 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -25,7 +25,6 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; /** * Violation of the alive inputs occurred, same location was marked as alive argument as well as @@ -33,8 +32,7 @@ */ @SuppressWarnings("serial") public class AliveConstraintViolationException extends RAVException { - public LIRInstruction instruction; - public BasicBlock block; + public RAVInstruction.Op instruction; /** * Construct an AliveConstraintViolationException. @@ -44,17 +42,16 @@ public class AliveConstraintViolationException extends RAVException { * @param location Location that is being shared * @param asDest Alive location was used as an output */ - public AliveConstraintViolationException(LIRInstruction instruction, BasicBlock block, RAValue location, boolean asDest) { - super(AliveConstraintViolationException.getErrorMessage(instruction, block, location, asDest)); + public AliveConstraintViolationException(RAVInstruction.Op instruction, BasicBlock block, RAValue location, boolean asDest) { + super(AliveConstraintViolationException.getErrorMessage(location, asDest), instruction, block); this.instruction = instruction; - this.block = block; } - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, RAValue location, boolean asDest) { + static String getErrorMessage(RAValue location, boolean asDest) { if (asDest) { - return "Location " + location + " used as both alive and output in " + instruction + " in block" + block; + return "Location " + location + " used as both alive and output"; } - return "Location " + location + " used as both alive and temp in " + instruction + "in block" + block; + return "Location " + location + " used as both alive and temp"; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java index 188cc775dc7c..48e575a0f6ce 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java @@ -103,8 +103,8 @@ public AllocationState get(RAValue key, AllocationState defaultValue) { * @param key Location used * @param state State to store */ - public void put(RAValue key, AllocationState state) { - this.checkRegisterDestinationValidity(key); + public void put(RAValue key, AllocationState state, RAVInstruction.Base instruction) { + this.checkRegisterDestinationValidity(key, instruction); putWithoutRegCheck(key, state); } @@ -131,13 +131,13 @@ public void putWithoutRegCheck(RAValue key, AllocationState state) { * @param key Location used * @param state State to store */ - public void putClone(RAValue key, AllocationState state) { + public void putClone(RAValue key, AllocationState state, RAVInstruction.Base instruction) { if (state.isUnknown()) { - this.put(key, state); + this.put(key, state, instruction); return; } - this.put(key, state.clone()); + this.put(key, state.clone(), instruction); } /** @@ -193,7 +193,7 @@ public boolean mergeWith(AllocationStateMap source) { * * @param location Value that could be a register. */ - protected void checkRegisterDestinationValidity(RAValue location) { + protected void checkRegisterDestinationValidity(RAValue location, RAVInstruction.Base instruction) { if (!location.isRegister()) { return; } @@ -201,7 +201,7 @@ protected void checkRegisterDestinationValidity(RAValue location) { // Equality check so we know that this change was made by the register allocator. var register = location.asRegister().getRegister(); if (!this.registerAllocationConfig.getAllocatableRegisters().contains(register)) { - throw new InvalidRegisterUsedException(register); + throw new InvalidRegisterUsedException(register, instruction, block); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 2fc4875b19ae..ef0d603996e7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -160,7 +160,7 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { return; } - throw new MissingLocationError(op.lirInstruction, block, orig); + throw new MissingLocationException(op, block, orig); } ValueKind currKind = curr.getValue().getValueKind(); @@ -171,7 +171,7 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } if (!kindsEqual(orig.getValue().getValueKind(), currKind)) { - throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); + throw new KindsMismatchException(op, block, orig, curr, true); } AllocationState state = this.values.get(curr); @@ -194,7 +194,7 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } if (state.isUnknown()) { - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + throw new ValueNotInRegisterException(op, block, orig, curr, state, this); } if (state.isConflicted()) { @@ -208,12 +208,12 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { - this.values.put(curr, resolvedState); + this.values.put(curr, resolvedState, op); return; } } - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + throw new ValueNotInRegisterException(op, block, orig, curr, state, this); } if (state instanceof ValueAllocationState valAllocState) { @@ -229,16 +229,16 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { - this.values.put(curr, resolvedState); + this.values.put(curr, resolvedState, op); return; } } - throw new ValueNotInRegisterException(op.lirInstruction, block, orig, curr, state); + throw new ValueNotInRegisterException(op, block, orig, curr, state, this); } if (!kindsEqualFromState(orig, valAllocState.value)) { - throw new KindsMismatchException(op.lirInstruction, block, orig, valAllocState.value, false); + throw new KindsMismatchException(op, block, orig, valAllocState.value, false); } return; @@ -344,7 +344,7 @@ protected void checkReferences(RAVInstruction.Op op) { if (state.isConflicted()) { var confState = (ConflictedAllocationState) state; for (var valAllocState : confState.getConflictedStates()) { - if (valAllocState.isIllegal()) { + if (valAllocState.isUndefinedFromBlock()) { continue; // Undefined in branch } @@ -352,14 +352,14 @@ protected void checkReferences(RAVInstruction.Op op) { continue; // Is a reference } - throw new MissingReferenceException(reference, state, op, block); + throw new MissingReferenceException(op, block, reference, state, this); } continue; } if (state.isUnknown()) { - throw new MissingReferenceException(reference, state, op, block); + throw new MissingReferenceException(op, block, reference, state, this); } var valAllocState = (ValueAllocationState) state; @@ -367,7 +367,7 @@ protected void checkReferences(RAVInstruction.Op op) { continue; // Is a reference } - throw new MissingReferenceException(reference, state, op, block); + throw new MissingReferenceException(op, block, reference, state, this); } } @@ -388,7 +388,7 @@ protected void checkLocationMoveKinds(RAVInstruction.LocationMove move) { return; } - throw new KindsMismatchException(move.lirInstruction, block, move.to, movedValue, false); + throw new KindsMismatchException(move, block, move.to, movedValue, false); } } } @@ -402,7 +402,7 @@ protected void checkValueMoveKinds(RAVInstruction.ValueMove move) { } if (!kindsEqual(move.getLocation(), move.variableOrConstant)) { - throw new KindsMismatchException(move.lirInstruction, block, move.getLocation(), move.variableOrConstant, false); + throw new KindsMismatchException(move, block, move.getLocation(), move.variableOrConstant, false); } } @@ -487,7 +487,7 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { continue; } - throw new RAVException(orig + " -> " + curr + " not an object java kind when marked as a reference"); + throw new RAVException(orig + " -> " + curr + " not an object java kind when marked as a reference", op, block); } else { if (origLIRKind.isValue() && currLIRKind.isValue()) { // Either not a reference, or a derived one - which might not be marked as @@ -495,7 +495,7 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { continue; } - throw new RAVException(orig + " -> " + curr + " is a reference when not marked as an object java kind"); + throw new RAVException(orig + " -> " + curr + " is a reference when not marked as an object java kind", op, block); } } } @@ -515,7 +515,7 @@ protected void checkTempKind(RAVInstruction.Op op) { if (!kindsEqual(orig, curr)) { // Make sure the assigned register has the correct kind for temp. - throw new KindsMismatchException(op.lirInstruction, block, orig, curr, true); + throw new KindsMismatchException(op, block, orig, curr, true); } } } @@ -540,13 +540,13 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction) { for (int j = 0; j < instruction.temp.count; j++) { if (value.equals(instruction.temp.curr[j])) { - throw new AliveConstraintViolationException(instruction.lirInstruction, block, value, false); + throw new AliveConstraintViolationException(instruction, block, value, false); } } for (int j = 0; j < instruction.dests.count; j++) { if (value.equals(instruction.dests.curr[j])) { - throw new AliveConstraintViolationException(instruction.lirInstruction, block, value, true); + throw new AliveConstraintViolationException(instruction, block, value, true); } } } @@ -611,13 +611,13 @@ public void update(RAVInstruction.Base instruction) { public void updateWithLocationMove(RAVInstruction.LocationMove move) { if (move instanceof RAVInstruction.StackMove stackMove) { // Maybe the backup slot should hold what the scratch register holds? - this.values.put(stackMove.backupSlot, UnknownAllocationState.INSTANCE); + this.values.put(stackMove.backupSlot, UnknownAllocationState.INSTANCE, move); } var state = this.values.get(move.from); if (move.validateRegisters) { - this.values.putClone(move.to, state); + this.values.putClone(move.to, state, move); } else { this.values.putWithoutRegCheck(move.to, state.clone()); } @@ -674,7 +674,7 @@ protected void updateWithOp(RAVInstruction.Op op) { // allocator this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op, block)); } else { - this.values.put(location, new ValueAllocationState(variable, op, block)); + this.values.put(location, new ValueAllocationState(variable, op, block), op); } } @@ -692,7 +692,7 @@ protected void updateWithOp(RAVInstruction.Op op) { if (op.temp.orig[i].equals(location)) { this.values.putWithoutRegCheck(location, UnknownAllocationState.INSTANCE); } else { - this.values.put(location, UnknownAllocationState.INSTANCE); + this.values.put(location, UnknownAllocationState.INSTANCE, op); } } @@ -827,7 +827,7 @@ protected void checkCalleeSavedRegisters() { } } - throw new CalleeSavedRegisterNotRetrievedException(regValue, block); + throw new CalleeSavedRegisterNotRetrievedException(regValue, block, this); } } @@ -863,7 +863,7 @@ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove) { this.values.putWithoutRegCheck(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); } else { - this.values.put(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); + this.values.put(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block), valueMove); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java index 4f1eee671dc7..8caeb1e25d26 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java @@ -27,18 +27,19 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; /** - * Callee-saved register was not retrieved on exit block. + * Callee-saved register was not retrieved on an exit block. */ @SuppressWarnings("serial") public class CalleeSavedRegisterNotRetrievedException extends RAVException { public RARegister register; - public BasicBlock block; + public BlockVerifierState blockVerifierState; - public CalleeSavedRegisterNotRetrievedException(RARegister register, BasicBlock block) { - super(getErrorMessage(register, block)); + public CalleeSavedRegisterNotRetrievedException(RARegister register, BasicBlock block, BlockVerifierState blockVerifierState) { + super(getErrorMessage(register), null, block); + this.blockVerifierState = new BlockVerifierState(block, blockVerifierState); } - public static String getErrorMessage(RARegister register, BasicBlock block) { - return "Callee saved register " + register + " not retrieved on exit " + block; + public static String getErrorMessage(RARegister register) { + return "Callee saved register " + register + " not retrieved on exit"; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 6e2a14352506..541fff7d0831 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -27,7 +27,6 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.util.EconomicHashSet; -import java.util.Arrays; import java.util.Set; /** @@ -110,8 +109,8 @@ public AllocationState meet(AllocationState other, BasicBlock otherBlock, Bas // the unknown state is coming from a different predecessor to the same block, // and it means that this location was not defined there, but it was defined in a // different predecessor block, meaning it's now in a conflicted state, where - // it either is defined or it is not - should not be used in further blocks. - newlyConflictedState.addConflictedValue(ValueAllocationState.createIllegal(otherBlock)); + // it either is defined or it - should not be used in further blocks. + newlyConflictedState.addConflictedValue(ValueAllocationState.createUndefined(otherBlock)); } return newlyConflictedState; @@ -123,11 +122,11 @@ public AllocationState clone() { } /** - * We do not compare conflicted state on its contents, whenever new one would be created as a - * result, the set of contents would remain the same, if values are equal based on RAValue + * We do not compare a conflicted state on its contents, whenever a new one would be created as + * a result, the set of contents would remain the same, if values are equal based on RAValue * rules. * - * @param other Other state we are comparing it to + * @param other Another state we are comparing it to * @return Are both states conflicted? */ @Override @@ -137,6 +136,15 @@ public boolean equals(AllocationState other) { @Override public String toString() { - return "Conflicted {" + Arrays.toString(this.conflictedStates.toArray()) + "}"; + StringBuilder sb = new StringBuilder("Conflicted {"); + for (var state : this.conflictedStates) { + sb.append(state.toString()).append(", "); + } + + if (!conflictedStates.isEmpty()) { + sb.setLength(sb.length() - 2); + } + + return sb.append("}").toString(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index df662791be87..fa7c080e8374 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -151,7 +151,7 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted } if (isRematerializedToWrongLocation(variable, constantState)) { - throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); + throw new ConstantRematerializedToStackException(variable, location, constantState); } return new ValueAllocationState(variable, constantState.getSource(), constantState.block); @@ -187,7 +187,7 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati } if (isRematerializedToWrongLocation(variable, valueState)) { - throw new RAVException("Variable " + variable + " cannot be rematerialized to stack location " + location); + throw new ConstantRematerializedToStackException(variable, location, valueState); } return new ValueAllocationState(variable, valueState.getSource(), valueState.getBlock()); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantRematerializedToStackException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantRematerializedToStackException.java new file mode 100644 index 000000000000..c2e4fa5707a2 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantRematerializedToStackException.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +/** + * Constant was rematerialized to a stack location, which was forbidden for this variable/constant. + */ +@SuppressWarnings("serial") +public class ConstantRematerializedToStackException extends RAVException { + public RAVariable variable; + public RAValue location; + public ValueAllocationState state; + + public ConstantRematerializedToStackException(RAVariable variable, RAValue location, ValueAllocationState state) { + super("Variable " + variable + " cannot be rematerialized to stack location " + location, state.getSource(), state.getBlock()); + + this.variable = variable; + this.location = location; + this.state = state; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java index 2d47e7fc486c..f27944ea67bf 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.vm.ci.code.Register; /** @@ -34,8 +35,8 @@ public class InvalidRegisterUsedException extends RAVException { public Register register; - public InvalidRegisterUsedException(Register register) { - super(getErrorMessage(register)); + public InvalidRegisterUsedException(Register register, RAVInstruction.Base instruction, BasicBlock block) { + super(getErrorMessage(register), instruction, block); this.register = register; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java index 31bf90688dec..9422b8f5cbc7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -25,15 +25,12 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; /** * Kinds are not matching between two values. */ @SuppressWarnings("serial") public class KindsMismatchException extends RAVException { - public LIRInstruction instruction; - public BasicBlock block; public RAValue value1; public RAValue value2; public boolean origVsCurr; @@ -47,21 +44,19 @@ public class KindsMismatchException extends RAVException { * @param value2 Second value in comparison, either current location or value stored in state * @param origVsCurr Comparing original variable to current location */ - public KindsMismatchException(LIRInstruction instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { - super(KindsMismatchException.getErrorMessage(instruction, block, value1, value2, origVsCurr)); + public KindsMismatchException(RAVInstruction.Base instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { + super(KindsMismatchException.getErrorMessage(value1, value2, origVsCurr), instruction, block); - this.instruction = instruction; - this.block = block; this.value1 = value1; this.value2 = value2; this.origVsCurr = origVsCurr; } - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { + static String getErrorMessage(RAValue value1, RAValue value2, boolean origVsCurr) { if (origVsCurr) { - return value1.getValue() + " has different kind after allocation: " + value2.getValue() + " in " + instruction + " in block " + block; + return value1.getValue() + " has different kind after allocation: " + value2.getValue(); } - return "Value in location has different kind: " + value1.getValue().getValueKind() + " vs. " + value2.getValue().getValueKind() + " in " + instruction + " in block " + block; + return "Value in location has different kind: " + value1.getValue().getValueKind() + " vs. " + value2.getValue().getValueKind(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationException.java similarity index 74% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationException.java index 62c84ca4f529..1fb845ee0b26 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationException.java @@ -25,25 +25,24 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; /** * No location found in an instruction after allocation for certain variable. */ @SuppressWarnings("serial") -public class MissingLocationError extends RAVError { +public class MissingLocationException extends RAVException { /** * Construct a MissingLocationError. * * @param instruction Instruction where violation occurred * @param block Block where violation occurred - * @param variable Variable before allocation, that has no location afterward + * @param variable Variable before allocation that has no location afterward */ - public MissingLocationError(LIRInstruction instruction, BasicBlock block, RAValue variable) { - super(MissingLocationError.getMessage(instruction, block, variable)); + public MissingLocationException(RAVInstruction.Op instruction, BasicBlock block, RAValue variable) { + super(MissingLocationException.getMessage(variable), instruction, block); } - static String getMessage(LIRInstruction instruction, BasicBlock block, RAValue variable) { - return "Variable " + variable + " is missing a location in " + instruction + " in block " + block; + static String getMessage(RAValue variable) { + return "Variable " + variable + " is missing a location"; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java index 5e6c03a5ac32..d6a3be1f7989 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java @@ -31,17 +31,17 @@ public class MissingReferenceException extends RAVException { public RAValue reference; public AllocationState state; public RAVInstruction.Op op; - public BasicBlock block; + public BlockVerifierState blockVerifierState; - public MissingReferenceException(RAValue reference, AllocationState state, RAVInstruction.Op op, BasicBlock block) { - super(getMessage(reference, state, op, block)); + public MissingReferenceException(RAVInstruction.Op op, BasicBlock block, RAValue reference, AllocationState state, BlockVerifierState blockVerifierState) { + super(getMessage(reference, state), op, block); this.reference = reference; this.state = state; this.op = op; - this.block = block; + this.blockVerifierState = new BlockVerifierState(block, blockVerifierState); } - public static String getMessage(RAValue reference, AllocationState state, RAVInstruction.Op op, BasicBlock block) { - return "Missing reference in " + reference + " in " + op.lirInstruction + " in " + block + " actually is " + state; + public static String getMessage(RAValue reference, AllocationState state) { + return "Missing reference in " + reference + " actually is " + state; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java index 63090a3cb82a..2528a61c6893 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java @@ -38,19 +38,16 @@ @SuppressWarnings("serial") public class OperandFlagMismatchException extends RAVException { public RAVInstruction.Op instruction; - public BasicBlock block; public Value value; public EnumSet flags; public OperandFlagMismatchException(RAVInstruction.Op op, BasicBlock block, Value value, EnumSet flags) { - super(getErrorMesage(op, block, value, flags)); - this.instruction = op; - this.block = block; + super(getErrorMessage(value, flags), op, block); this.value = value; this.flags = flags; } - static String getErrorMesage(RAVInstruction.Op op, BasicBlock block, Value value, EnumSet flags) { - return "Value " + value + " does not satisfy operand flags " + flags.toString() + " in " + op.lirInstruction + " in " + block; + static String getErrorMessage(Value value, EnumSet flags) { + return "Value " + value + " does not satisfy operand flags " + flags.toString(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java index 6c58535da76c..fdeaae13dfab 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; /** @@ -33,11 +34,33 @@ */ @SuppressWarnings("serial") public class RAVException extends GraalError { - public RAVException(String message) { + public RAVInstruction.Base instruction; + public BasicBlock block; + + public RAVException(String message, RAVInstruction.Base instruction, BasicBlock block) { super(message); + + this.instruction = instruction; + this.block = block; + } + + public RAVException(String message, RAVException cause) { + super(cause, message); + + this.instruction = cause.instruction; + this.block = cause.block; + } + + @Override + public synchronized RAVException getCause() { + return (RAVException) super.getCause(); + } + + public String getLocationString() { + return instruction + " in " + block; } - public RAVException(String message, Throwable cause) { - super(message, cause); + public String getFullMessage() { + return getMessage() + " in " + getLocationString(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java index ee1d715aff2d..c02ff88a7ccf 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -24,6 +24,8 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.debug.GraalError; + import java.util.List; /** @@ -32,9 +34,13 @@ * to one exception. */ @SuppressWarnings("serial") -public class RAVFailedVerificationException extends RAVException { +public class RAVFailedVerificationException extends GraalError { + public List exceptions; + public RAVFailedVerificationException(String compUnitName, List exceptions) { super(RAVFailedVerificationException.getMessage(compUnitName, exceptions)); + + this.exceptions = exceptions; } static String getMessage(String compUnitName, List exceptions) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 3c9479307751..273217809b27 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -195,10 +195,13 @@ public boolean verifyContents() { public String toString() { StringBuilder result = new StringBuilder("["); for (int i = 0; i < this.count; i++) { - if (this.curr[i] != null) { - result.append(this.orig[i].toString()).append(" -> ").append(this.curr[i].toString()); + var origVar = this.orig[i]; + var currLoc = this.curr[i]; + + if (currLoc != null) { + result.append(origVar.getValue().toString()).append(" -> ").append(currLoc.getValue().toString()); } else { - result.append(this.orig[i].toString()); + result.append(origVar.getValue().toString()); } result.append(", "); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 6a7720f8e964..800b1e6344f3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -29,6 +29,7 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; +import java.io.OutputStream; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; @@ -157,7 +158,7 @@ protected BlockVerifierState createNewBlockState(BasicBlock block) { public void verifyInstructionInputs() { for (var blockId : this.lir.getBlocks()) { var block = this.lir.getBlockById(blockId); - var state = this.blockEntryStates.get(block); + var state = new BlockVerifierState(block, this.blockEntryStates.get(block)); var instructions = this.blockInstructions.get(block); for (var instr : instructions) { @@ -230,4 +231,8 @@ public void run() { this.calculateEntryBlocks(); this.verifyInstructionInputs(); } + + public VerifierPrinter getPrinter(OutputStream out) { + return new VerifierPrinter(out, this); + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index ca2041091f1e..91c8e1877103 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -279,17 +279,14 @@ protected void verifyAllocation(LIR lir, Map otherBlock, Bas if (other instanceof ConflictedAllocationState conflictedState) { var newConfState = new ConflictedAllocationState(conflictedState.conflictedStates); - newConfState.addConflictedValue(ValueAllocationState.createIllegal(block)); + newConfState.addConflictedValue(ValueAllocationState.createUndefined(block)); return newConfState; } - return new ConflictedAllocationState((ValueAllocationState) other, ValueAllocationState.createIllegal(null)); + return new ConflictedAllocationState((ValueAllocationState) other, ValueAllocationState.createUndefined(null)); } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index bdb3c92125f6..b98d337d7ca4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -72,14 +72,10 @@ public ValueAllocationState(ValueAllocationState other) { * * @return instance of {@link ValueAllocationState} holding {@link Value#ILLEGAL}. */ - public static ValueAllocationState createIllegal(BasicBlock block) { + public static ValueAllocationState createUndefined(BasicBlock block) { return new ValueAllocationState(new RAValue(Value.ILLEGAL), null, block); } - public boolean isIllegal() { - return value.isIllegal(); - } - public Value getValue() { return value.getValue(); } @@ -113,7 +109,7 @@ public AllocationState meet(AllocationState other, BasicBlock otherBlock, Bas // Unknown is coming from different predecessor where this location // is undefined, meaning this value is not always accessible in the successor // and thus conflict is created. - return new ConflictedAllocationState(createIllegal(otherBlock), this); + return new ConflictedAllocationState(createUndefined(otherBlock), this); } if (other.isConflicted()) { @@ -144,6 +140,10 @@ public ValueAllocationState clone() { @Override public String toString() { + if (isUndefinedFromBlock()) { + return "Value {undefined from " + block + "}"; + } + return "Value {" + this.value + "}"; } @@ -151,4 +151,8 @@ public String toString() { public int hashCode() { return this.value.hashCode(); } + + public boolean isUndefinedFromBlock() { + return Value.ILLEGAL.equals(this.value.getValue()) && source == null; + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 129dbd51e451..128f3db03f41 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -25,18 +25,17 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.lir.LIRInstruction; /** * Value was not found in the location we needed it in. */ @SuppressWarnings("serial") public class ValueNotInRegisterException extends RAVException { - public LIRInstruction instruction; - public BasicBlock block; + public RAVInstruction.Op instruction; public RAValue variable; // Can be a constant or other symbolic value public RAValue location; // Can be StackSlot, RegisterValue or memory public AllocationState state; + public BlockVerifierState blockVerifierState; /** * Construct a ValueNotInRegisterException. @@ -47,42 +46,16 @@ public class ValueNotInRegisterException extends RAVException { * @param location Location where we couldn't find it * @param state The actual state that the location is in */ - public ValueNotInRegisterException(LIRInstruction instruction, BasicBlock block, RAValue variable, RAValue location, AllocationState state) { - super(ValueNotInRegisterException.getErrorMessage(instruction, block, variable, location, state)); + public ValueNotInRegisterException(RAVInstruction.Op instruction, BasicBlock block, RAValue variable, RAValue location, AllocationState state, BlockVerifierState blockVerifierState) { + super(ValueNotInRegisterException.getErrorMessage(variable, location, state), instruction, block); - this.instruction = instruction; - this.block = block; this.variable = variable; this.location = location; this.state = state; + this.blockVerifierState = new BlockVerifierState(block, blockVerifierState); } - static String getErrorMessage(LIRInstruction instruction, BasicBlock block, RAValue variable, RAValue location, AllocationState state) { - var messageBuilder = new StringBuilder(); - // @formatter:off - messageBuilder - .append("Value ") - .append(variable) - .append(" not found in ") - .append(location) - .append(" for instruction ") - .append(instruction) - .append(" in ") - .append(block) - .append(" actual state is "); - // @formatter:on - - if (state instanceof ConflictedAllocationState confState) { - var confStates = confState.getConflictedStates(); - - messageBuilder.append("\n"); - for (var conflictedState : confStates) { - messageBuilder.append(" - ").append(conflictedState.getRAValue()).append(" from ").append(conflictedState.source).append(" in ").append(conflictedState.block).append("\n"); - } - } else { - messageBuilder.append(state); - } - - return messageBuilder.toString(); + static String getErrorMessage(RAValue variable, RAValue location, AllocationState state) { + return "Value " + variable + " not found in " + location + " the actual state is " + state; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index 7b5fd13c9b13..df774a59d0f0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -25,49 +25,28 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.debug.LogStream; -import java.io.PrintStream; -import java.util.List; +import java.io.OutputStream; public class VerifierPrinter { public static int PADDING = 4; + public static int INDENT = 4; - /** - * Print human-readable representation of the Verifier IR to an output stream. - * - * @param out Output stream - * @param lir LIR - * @param instructions Verifier IR - */ - public static void print(PrintStream out, LIR lir, BlockMap> instructions) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - - printBlockHeader(out, block); - for (var instruction : instructions.get(block)) { - out.println("\t" + instruction.toString() + " | " + instruction.getLIRInstruction().toString()); - if (instruction instanceof RAVInstruction.Op op) { - if (op.lirInstruction.hasState()) { - out.println("\t\t State: " + op.stateValues); - } + protected LogStream out; + protected RegAllocVerifier verifier; - if (op.references != null) { - out.println("\t\t References: " + op.references); - } - } - } - out.println(); - } + protected VerifierPrinter(OutputStream out, RegAllocVerifier verifier) { + this.out = new LogStream(out); + this.verifier = verifier; } - public static void printAligned(PrintStream out, LIR lir, BlockMap> instructions) { + public void print() { int longestRAVInstruction = 0; - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); + for (var blockId : verifier.lir.getBlocks()) { + var block = verifier.lir.getBlockById(blockId); - for (var instruction : instructions.get(block)) { + for (var instruction : verifier.blockInstructions.get(block)) { int instructionLength = instruction.toString().length(); if (instructionLength > longestRAVInstruction) { longestRAVInstruction = instruction.toString().length(); @@ -75,69 +54,54 @@ public static void printAligned(PrintStream out, LIR lir, BlockMap> instructions) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - - printBlockHeader(out, block); - int n = 1; - for (var instruction : instructions.get(block)) { - out.println("\t" + n + "." + instruction.toString()); + out.println(instructionString + space + lirInstrString); if (instruction instanceof RAVInstruction.Op op) { + out.adjustIndentation(INDENT); if (op.lirInstruction.hasState()) { - out.println("\t\t State: " + op.stateValues); + out.println("State: " + op.stateValues); } if (op.references != null) { - out.println("\t\t References: " + op.references); + out.println("References: " + op.references); } + out.adjustIndentation(-INDENT); } - n++; - } - - out.println(); - - n = 1; - for (var instruction : instructions.get(block)) { - out.println("\t" + n + "." + instruction.lirInstruction.toString()); - n++; } + out.adjustIndentation(-INDENT); out.println(); } } - protected static void printBlockHeader(PrintStream out, BasicBlock block) { + protected void printBlockHeader(BasicBlock block) { var blockHeaderSB = new StringBuilder(); blockHeaderSB.append(block.toString()).append(": "); if (block.getPredecessorCount() > 0) { + blockHeaderSB.append(block); blockHeaderSB.append(" <- "); for (int i = 0; i < block.getPredecessorCount(); i++) { blockHeaderSB.append(block.getPredecessorAt(i)).append(", "); @@ -149,6 +113,11 @@ protected static void printBlockHeader(PrintStream out, BasicBlock block) { } if (block.getSuccessorCount() > 0) { + if (block.getPredecessorCount() > 0) { + blockHeaderSB.append(" | "); + } + + blockHeaderSB.append(block); blockHeaderSB.append(" -> "); for (int i = 0; i < block.getSuccessorCount(); i++) { blockHeaderSB.append(block.getSuccessorAt(i)).append(", "); @@ -159,6 +128,245 @@ protected static void printBlockHeader(PrintStream out, BasicBlock block) { } } - out.println(blockHeaderSB); + if (block.isLoopHeader()) { + blockHeaderSB.append(" | Loop {"); + var loop = block.getLoop(); + for (var member : loop.getBlocks()) { + blockHeaderSB.append(member); + blockHeaderSB.append(", "); + } + blockHeaderSB.setLength(blockHeaderSB.length() - 2); // always at least one member + blockHeaderSB.append("}"); + } + + out.println(blockHeaderSB.toString()); + } + + protected void printEntryState(BasicBlock block) { + var blockVerifierState = verifier.blockEntryStates.get(block); + + if (block.getId() == 0) { + return; + } + + out.println("Entry state:"); + out.adjustIndentation(INDENT); + for (var location : blockVerifierState.values.internalMap.keySet()) { + var state = blockVerifierState.values.get(location); + if (state.isUnknown()) { + continue; + } + out.println(location + " -> " + state); + } + out.adjustIndentation(-INDENT); + out.println(); + } + + protected void printAllocationState(RAValue location, AllocationState state) { + String stateStr = switch (state) { + case ValueAllocationState st -> { + if (st.isUndefinedFromBlock()) { + // Undefined value from a certain block. + yield "Value unknown from " + st.block; + } else { + yield "Value {" + st.getValue() + "} from " + st.source + " in " + st.block; + } + } + case ConflictedAllocationState st -> { + StringBuilder str = new StringBuilder(); + str.append("Conflicted: \n"); + for (var valueAllocState : st.getConflictedStates()) { + if (valueAllocState.isUndefinedFromBlock()) { + str.append(" - Value unknown from ").append(valueAllocState.block); + continue; + } + + str.append(" - ").append(valueAllocState.getValue()).append(" from ").append(valueAllocState.source).append(" in ").append(valueAllocState.block).append("\n"); + } + yield str.toString(); + } + case UnknownAllocationState st -> "Unknown"; + default -> throw new RAVError("Unexpected value: " + state); + }; + + out.println(location + " -> " + stateStr); + } + + protected void printIRWithException(RAVException exception) { + out.println("Register Allocation Verification failure:"); + out.println(exception.getMessage()); + out.println("Sourced from " + exception.getLocationString()); + out.println(); + + for (var blockId : verifier.lir.getBlocks()) { + var block = verifier.lir.getBlockById(blockId); + + printBlockHeader(block); + if (block.equals(exception.block)) { + out.println("^------------ Exception thrown here"); + out.println(); + } + + out.adjustIndentation(INDENT); + printEntryState(block); + for (var instruction : verifier.blockInstructions.get(block)) { + if (exception.instruction == instruction) { + out.println(""); + } + + printInstruction(instruction); + if (exception.instruction == instruction) { + printExceptionInformation(exception); + } + } + + if (exception.instruction == null) { + printExceptionInformation(exception); + } + + out.adjustIndentation(-INDENT); + out.println(); + } + } + + protected void printIRWithMultiExceptions(RAVFailedVerificationException exception) { + out.println("Register Allocation Verification failure:"); + out.println(exception.getMessage()); + out.println(); + + for (var blockId : verifier.lir.getBlocks()) { + var block = verifier.lir.getBlockById(blockId); + + printBlockHeader(block); + + boolean inBlock = false; + for (var e : exception.exceptions) { + if (block.equals(e.block)) { + inBlock = true; + break; + } + } + + if (inBlock) { + out.println("^------------ Exception thrown here"); + out.println(); + } + + out.adjustIndentation(INDENT); + printEntryState(block); + for (var instruction : verifier.blockInstructions.get(block)) { + + boolean inInstruction = false; + for (var e : exception.exceptions) { + if (e.instruction == instruction) { + inInstruction = true; + break; + } + } + + if (inInstruction) { + out.println(""); + } + + printInstruction(instruction); + for (var e : exception.exceptions) { + if (e.instruction == instruction) { + printExceptionInformation(e); + } + } + } + + for (var e : exception.exceptions) { + if (e.instruction == null) { + printExceptionInformation(e); + } + } + + out.adjustIndentation(-INDENT); + out.println(); + } + } + + protected void printInstruction(RAVInstruction.Base instruction) { + out.println(instruction.toString()); + if (instruction instanceof RAVInstruction.Op op) { + out.adjustIndentation(INDENT); + if (op.lirInstruction.hasState()) { + out.println("State: " + op.stateValues); + } + + if (op.references != null) { + StringBuilder refString = new StringBuilder("References: "); + for (var reference : op.references) { + refString.append(reference.getValue()).append(", "); + } + + if (!op.references.isEmpty()) { + refString.setLength(refString.length() - 2); + } + + out.println(refString.toString()); + } + + out.adjustIndentation(-INDENT); + } + } + + protected void printExceptionInformation(RAVException exception) { + out.println("^-------- " + exception.getMessage()); + + switch (exception) { + case ValueNotInRegisterException e -> printOtherStates(e); + case CalleeSavedRegisterNotRetrievedException e -> printCalleeSavedValues(e); + case MissingReferenceException e -> printOtherReferences(e); + default -> { + } + } + + out.println(); + } + + private void printOtherStates(ValueNotInRegisterException exception) { + out.println("Other states:"); + + var locations = exception.blockVerifierState.values.getValueLocations(exception.variable); + out.adjustIndentation(INDENT); + for (var location : locations) { + var state = exception.blockVerifierState.values.get(location); + printAllocationState(location, state); + } + out.adjustIndentation(-INDENT); + } + + private void printCalleeSavedValues(CalleeSavedRegisterNotRetrievedException exception) { + out.println("Callee-saved values:"); + var registers = verifier.registerAllocationConfig.getRegisterConfig().getCalleeSaveRegisters(); + out.adjustIndentation(INDENT); + for (var reg : registers) { + var regValue = new RARegister(reg.asValue()); + var state = exception.blockVerifierState.values.get(regValue); + printAllocationState(regValue, state); + } + out.adjustIndentation(-INDENT); + } + + private void printOtherReferences(MissingReferenceException exception) { + out.println("Other references:"); + out.adjustIndentation(INDENT); + for (var location : exception.blockVerifierState.values.internalMap.keySet()) { + var state = exception.blockVerifierState.values.get(location); + if (state.isUnknown() || state.isConflicted()) { + continue; + } + + var valueAllocState = (ValueAllocationState) state; + if (valueAllocState.getRAValue().getLIRKind().isValue()) { + continue; + } + + // Print all available references. + printAllocationState(location, state); + } + out.adjustIndentation(-INDENT); } } From ea465ec921359fcef431b3a1bd4e73c9975b8e2a Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 1 Apr 2026 23:28:34 +0200 Subject: [PATCH 099/112] Add test for infinite loop --- .../core/test/RegAllocVerifierTest.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index 5c0ea034055d..b4e0fe2cf7ab 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -777,6 +777,10 @@ protected void compileModified(String name) { } protected void assertException(Class expected) { + if (exception.getCause() != null) { + exception = exception.getCause(); + } + Assert.assertNotNull("No exception was thrown", exception); Assert.assertTrue("Unexpected exception: " + exception, expected.isInstance(exception)); } @@ -856,6 +860,16 @@ public static int aliveConstraintSnippet(Ex e) { } } + public static void loop(int a, int b) { + while (true) { + if (a > 3) { + System.out.println("a = " + a); + } + + a += b; + } + } + @Before public void prepareTest() { exception = null; @@ -972,6 +986,30 @@ public void testConflictInLoop() { } } + @Test + public void testConflictInInfiniteLoop() { + var loopConflictPhase = new LoopConflictPhase(); + phase = loopConflictPhase; + + var methodName = "loop"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(ValueNotInRegisterException.class); + var vnrException = (ValueNotInRegisterException) exception; + Assert.assertTrue(vnrException.state.isConflicted()); + + var confState = (ConflictedAllocationState) vnrException.state; + var conflitedStates = confState.getConflictedStates(); + Assert.assertEquals(2, conflitedStates.size()); + for (var state : conflitedStates) { + Assert.assertTrue(state.getRAValue().equals(loopConflictPhase.newVariable) || state.getRAValue().equals(loopConflictPhase.targetVariable)); + } + } + @Test public void testAliveConstraintInDest() { var violateAliveConstraintPhase = new ViolateAliveConstraintInDstPhase(); From 25596b833f664dfb332d9dd3ca661c98d46bc084 Mon Sep 17 00:00:00 2001 From: danocmx Date: Fri, 3 Apr 2026 22:35:55 +0200 Subject: [PATCH 100/112] Add test for callee-saved registers --- .../core/test/RegAllocVerifierTest.java | 102 ++++++++++++++++++ ...leeSavedRegisterNotRetrievedException.java | 1 + 2 files changed, 103 insertions(+) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index b4e0fe2cf7ab..1925043f1f8f 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -34,6 +34,7 @@ import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.lir.alloc.RegisterAllocationPhase; import jdk.graal.compiler.lir.alloc.verifier.AliveConstraintViolationException; +import jdk.graal.compiler.lir.alloc.verifier.CalleeSavedRegisterNotRetrievedException; import jdk.graal.compiler.lir.alloc.verifier.ConflictedAllocationState; import jdk.graal.compiler.lir.alloc.verifier.InvalidRegisterUsedException; import jdk.graal.compiler.lir.alloc.verifier.KindsMismatchException; @@ -50,10 +51,19 @@ import jdk.graal.compiler.lir.phases.LIRSuites; import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.util.EconomicHashMap; +import jdk.vm.ci.amd64.AMD64; +import jdk.vm.ci.aarch64.AArch64; +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.RegisterValue; import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.code.ValueKindFactory; import jdk.vm.ci.code.ValueUtil; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.PlatformKind; import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; import org.junit.Assert; @@ -706,6 +716,81 @@ protected BasicBlock getConflictSourceBlock(LIR lir, BasicBlock loopBlock) } } + class CalleeSavePhase extends RAVPhaseWrapper { + Register register; + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { + var name = target.arch.getName(); + + switch (name) { + case "AMD64" -> register = AMD64.rsi; + case "ARM64" -> register = AArch64.r0; + } + + super.run(target, lirGenRes, context); + } + + @Override + protected RegisterAllocationConfig getRegisterAllocationConfig(AllocationContext context) { + var regCfg = context.registerAllocationConfig.getRegisterConfig(); + var newRegCfg = new RegisterConfig() { + + @Override + public Register getReturnRegister(JavaKind kind) { + return regCfg.getReturnRegister(kind); + } + + @Override + public Register getFrameRegister() { + return regCfg.getFrameRegister(); + } + + @Override + public CallingConvention getCallingConvention(CallingConvention.Type type, JavaType returnType, JavaType[] parameterTypes, ValueKindFactory valueKindFactory) { + return regCfg.getCallingConvention(type, returnType, parameterTypes, valueKindFactory); + } + + @Override + public List getCallingConventionRegisters(CallingConvention.Type type, JavaKind kind) { + return regCfg.getCallingConventionRegisters(type, kind); + } + + @Override + public List getAllocatableRegisters() { + return regCfg.getAllocatableRegisters(); + } + + @Override + public List filterAllocatableRegisters(PlatformKind kind, List registers) { + return regCfg.filterAllocatableRegisters(kind, registers); + } + + @Override + public List getCallerSaveRegisters() { + return regCfg.getCallerSaveRegisters(); + } + + @Override + public List getCalleeSaveRegisters() { + return List.of(register); + } + + @Override + public List getAttributesMap() { + return regCfg.getAttributesMap(); + } + + @Override + public boolean areAllAllocatableRegistersCallerSaved() { + return regCfg.areAllAllocatableRegistersCallerSaved(); + } + }; + + return new RegisterAllocationConfig(newRegCfg, null); + } + } + @Override protected LIRSuites createLIRSuites(OptionValues options) { if (validSuites) { @@ -1080,6 +1165,23 @@ public void testKindMatchInState() { Assert.assertEquals(ValueKind.Illegal, kmException.value2.getValue().getValueKind()); Assert.assertFalse(kmException.origVsCurr); } + + @Test + public void testCalleeSaveRetrieval() { + var calleeSavePhase = new CalleeSavePhase(); + phase = calleeSavePhase; + + var methodName = "simple"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(CalleeSavedRegisterNotRetrievedException.class); + var csException = (CalleeSavedRegisterNotRetrievedException) exception; + Assert.assertEquals(csException.register.getRegister(), calleeSavePhase.register); + } } // Taken from EnumSwitchTest, shortened diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java index 8caeb1e25d26..1e447b2ee335 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java @@ -36,6 +36,7 @@ public class CalleeSavedRegisterNotRetrievedException extends RAVException { public CalleeSavedRegisterNotRetrievedException(RARegister register, BasicBlock block, BlockVerifierState blockVerifierState) { super(getErrorMessage(register), null, block); + this.register = register; this.blockVerifierState = new BlockVerifierState(block, blockVerifierState); } From 6180508a9a473e89134f92af3fdb34262228ce43 Mon Sep 17 00:00:00 2001 From: danocmx Date: Sat, 4 Apr 2026 12:46:18 +0200 Subject: [PATCH 101/112] Add tests for other exceptions --- .../core/test/RegAllocVerifierTest.java | 423 ++++++++++++++++++ 1 file changed, 423 insertions(+) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index 1925043f1f8f..e834fe371076 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -24,11 +24,15 @@ */ package jdk.graal.compiler.core.test; +import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.LIRInstructionClass; +import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.ValueProcedure; import jdk.graal.compiler.lir.Variable; @@ -36,8 +40,12 @@ import jdk.graal.compiler.lir.alloc.verifier.AliveConstraintViolationException; import jdk.graal.compiler.lir.alloc.verifier.CalleeSavedRegisterNotRetrievedException; import jdk.graal.compiler.lir.alloc.verifier.ConflictedAllocationState; +import jdk.graal.compiler.lir.alloc.verifier.ConstantRematerializedToStackException; import jdk.graal.compiler.lir.alloc.verifier.InvalidRegisterUsedException; import jdk.graal.compiler.lir.alloc.verifier.KindsMismatchException; +import jdk.graal.compiler.lir.alloc.verifier.MissingLocationException; +import jdk.graal.compiler.lir.alloc.verifier.MissingReferenceException; +import jdk.graal.compiler.lir.alloc.verifier.OperandFlagMismatchException; import jdk.graal.compiler.lir.alloc.verifier.RAVException; import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; import jdk.graal.compiler.lir.alloc.verifier.RAValue; @@ -45,14 +53,18 @@ import jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase; import jdk.graal.compiler.lir.alloc.verifier.ValueAllocationState; import jdk.graal.compiler.lir.alloc.verifier.ValueNotInRegisterException; +import jdk.graal.compiler.lir.asm.CompilationResultBuilder; +import jdk.graal.compiler.lir.framemap.SimpleVirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.AllocationPhase; import jdk.graal.compiler.lir.phases.LIRPhase; import jdk.graal.compiler.lir.phases.LIRSuites; import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.util.EconomicHashMap; +import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.aarch64.AArch64; +import jdk.vm.ci.amd64.AMD64Kind; import jdk.vm.ci.code.CallingConvention; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterAttributes; @@ -61,6 +73,9 @@ import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.code.ValueKindFactory; import jdk.vm.ci.code.ValueUtil; +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.JavaType; import jdk.vm.ci.meta.PlatformKind; @@ -74,6 +89,10 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.Set; + +import static jdk.graal.compiler.lir.LIRInstruction.OperandFlag.REG; +import static jdk.graal.compiler.lir.LIRInstruction.OperandFlag.STACK; public class RegAllocVerifierTest extends GraalCompilerTest { /** @@ -791,6 +810,330 @@ public boolean areAllAllocatableRegistersCallerSaved() { } } + class PhantomConstRematerializationPhase extends RAVPhaseWrapper { + RAVariable variableValue; + RAValue stackSlotValue; + ConstantValue constant; + + class LoadConstOp extends LIRInstruction implements StandardOp.LoadConstantOp { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(LoadConstOp.class); + + @Def({REG, STACK}) protected AllocatableValue result; + JavaConstant input; + + public LoadConstOp(AllocatableValue result, JavaConstant input) { + super(TYPE); + + this.result = result; + this.input = input; + } + + @Override + public Constant getConstant() { + return input; + } + + @Override + public boolean canRematerializeToStack() { + return false; + } + + @Override + public AllocatableValue getResult() { + return result; + } + + @Override + public void emitCode(CompilationResultBuilder crb) { + } + } + + @Override + protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { + var instructions = super.getVerifierInstructions(lir, preallocMap, ctx); + + var kind = LIRKind.value(AMD64Kind.V32_BYTE); + var jvConst = new JavaConstant() { + @Override + public JavaKind getJavaKind() { + return JavaKind.Int; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public boolean isDefaultForKind() { + return false; + } + + @Override + public Object asBoxedPrimitive() { + return null; + } + + @Override + public int asInt() { + return 0; + } + + @Override + public boolean asBoolean() { + return false; + } + + @Override + public long asLong() { + return 0; + } + + @Override + public float asFloat() { + return 0; + } + + @Override + public double asDouble() { + return 0; + } + }; + + constant = new ConstantValue(kind, jvConst); + var stackSlot = new SimpleVirtualStackSlot(1, kind); + var variable = new Variable(kind, lir.numVariables() + 1); + + stackSlotValue = RAValue.create(stackSlot); + variableValue = (RAVariable) RAValue.create(variable); + + var loadConstOp = new LoadConstOp(variable, constant.getJavaConstant()); + var constSpawnOp = new RAVInstruction.Op(loadConstOp); + loadConstOp.forEachOutput(constSpawnOp.dests.copyOriginalProc); + constSpawnOp.dests.curr[0] = stackSlotValue; + + var remMove = new RAVInstruction.ValueMove(new LoadConstOp(stackSlot, constant.getJavaConstant()), constant, stackSlot); + + var usage = new RAVInstruction.Op(new StandardOp.NoOp(null, 0)); + usage.uses = new RAVInstruction.ValueArrayPair(1); + usage.uses.curr[0] = stackSlotValue; + usage.uses.orig[0] = variableValue; + + var blockInstructions = instructions.get(0); + blockInstructions.add(1, constSpawnOp); + blockInstructions.add(blockInstructions.size() - 1, remMove); + blockInstructions.add(blockInstructions.size() - 1, usage); + + return instructions; + } + } + + class ForceOperandFlagPhase extends RAVPhaseWrapper { + @Override + protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { + var instructions = super.getVerifierInstructions(lir, preallocMap, ctx); + + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructionsForBlock = instructions.get(block); + for (var instruction : instructionsForBlock) { + if (instruction instanceof RAVInstruction.Op op) { + if (op.isLabel()) { + continue; + } + + for (int i = 0; i < op.uses.count; i++) { + var curr = op.uses.curr[i]; + var orig = op.uses.orig[i]; + + if (orig.equals(curr)) { + continue; + } + + EnumSet opFlags = EnumSet.noneOf(LIRInstruction.OperandFlag.class); + opFlags.addAll(op.uses.operandFlags.get(i)); + + if (curr.isRegister()) { + opFlags.remove(LIRInstruction.OperandFlag.REG); + } else if (LIRValueUtil.isStackSlotValue(curr.getValue())) { + opFlags.remove(LIRInstruction.OperandFlag.STACK); + } else if (LIRValueUtil.isConstantValue(curr.getValue())) { + opFlags.remove(LIRInstruction.OperandFlag.CONST); + } else { + continue; + } + + op.uses.operandFlags.set(i, opFlags); + return instructions; + } + } + } + } + + return instructions; + } + } + + class ForceMissingLocationExceptionPhase extends RAVPhaseWrapper { + @Override + protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { + var instructions = super.getVerifierInstructions(lir, preallocMap, ctx); + + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructionsForBlock = instructions.get(block); + for (var instruction : instructionsForBlock) { + if (instruction instanceof RAVInstruction.Op op) { + if (op.isLabel()) { + continue; + } + + for (int i = 0; i < op.uses.count; i++) { + var curr = op.uses.curr[i]; + var orig = op.uses.orig[i]; + + if (orig.equals(curr)) { + continue; + } + + op.uses.curr[i] = null; + return instructions; + } + } + } + } + + return instructions; + } + } + + class ExcessReferencePhase extends RAVPhaseWrapper { + @Override + protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { + var instructions = super.getVerifierInstructions(lir, preallocMap, ctx); + + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructionsForBlock = instructions.get(block); + + RAVInstruction.Op prev = null; + for (var instruction : instructionsForBlock) { + if (instruction instanceof RAVInstruction.Op op) { + if (prev != null) { + var curr = prev.dests.curr[0]; + var kind = curr.getLIRKind().makeUnknownReference(); + var refLocation = RAValue.create(curr.asRegister().getRegister().asValue(kind)); + + op.references = List.of(refLocation); + return instructions; + } + + if (op.isLabel()) { + continue; + } + + if (op.dests.count == 0) { + continue; + } + + var curr = op.dests.curr[0]; + if (!curr.isRegister() || !curr.getLIRKind().isValue()) { + continue; + } + + // This instruction will define dest location + // next instruction will verify the references list + // -> no reference there! + prev = op; + } + } + } + + return instructions; + } + } + + class PhiResolver extends RAVPhaseWrapper { + RAVInstruction.Op label; + RAValue location; + + @Override + protected BlockMap> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { + var instructions = super.getVerifierInstructions(lir, instrMap, context); + + Set usedRegisters = new EconomicHashSet<>(); + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructionsForBlock = instructions.get(block); + for (var instruction : instructionsForBlock) { + switch (instruction) { + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + var dest = op.dests.curr[i]; + if (dest.isRegister()) { + usedRegisters.add(dest.asRegister().getRegister()); + } + } + } + case RAVInstruction.ValueMove move -> { + var regValue = move.getLocation(); + if (regValue.isRegister()) { + usedRegisters.add(regValue.asRegister().getRegister()); + } + } + case RAVInstruction.LocationMove move -> { + if (move.to.isRegister()) { + usedRegisters.add(move.to.asRegister().getRegister()); + } + } + default -> { + } + } + } + } + + Register targetRegister = null; + var allocatableRegisters = context.registerAllocationConfig.getAllocatableRegisters(); + for (var register : allocatableRegisters) { + if (!usedRegisters.contains(register)) { + targetRegister = register; + break; + } + } + + assert targetRegister != null : "No register available for phi resolver test"; + + var labelOp = (RAVInstruction.Op) instructions.get(0).getFirst(); + + var newDst = new RAVInstruction.ValueArrayPair(labelOp.dests.count + 1); + for (int i = 0; i < labelOp.dests.count; i++) { + newDst.curr[i] = labelOp.dests.curr[i]; + newDst.orig[i] = labelOp.dests.orig[i]; + newDst.operandFlags.add(labelOp.dests.operandFlags.get(i)); + } + + var variable = RAVariable.create(new Variable(ValueKind.Illegal, lir.numVariables() + 1)); + location = RAValue.create(targetRegister.asValue()); + + newDst.orig[labelOp.dests.count] = variable; + newDst.curr[labelOp.dests.count] = null; + newDst.operandFlags.add(EnumSet.of(LIRInstruction.OperandFlag.REG)); + + labelOp.dests = newDst; + label = labelOp; + + var usage = new RAVInstruction.Op(new StandardOp.NoOp(null, 0)); + usage.uses = new RAVInstruction.ValueArrayPair(1); + usage.uses.orig[0] = variable; + usage.uses.curr[0] = location; + usage.uses.operandFlags.add(EnumSet.of(LIRInstruction.OperandFlag.REG)); + + instructions.get(0).add(instructions.get(0).size() - 1, usage); + + return instructions; + } + } + @Override protected LIRSuites createLIRSuites(OptionValues options) { if (validSuites) { @@ -1182,6 +1525,86 @@ public void testCalleeSaveRetrieval() { var csException = (CalleeSavedRegisterNotRetrievedException) exception; Assert.assertEquals(csException.register.getRegister(), calleeSavePhase.register); } + + @Test + public void testOperandFlags() { + var forceOperandFlagPhase = new ForceOperandFlagPhase(); + phase = forceOperandFlagPhase; + + var methodName = "simple"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(OperandFlagMismatchException.class); + } + + @Test + public void testMissingLocation() { + var forceMissingLocationException = new ForceMissingLocationExceptionPhase(); + phase = forceMissingLocationException; + + var methodName = "simple"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(MissingLocationException.class); + } + + @Test + public void testExcessReference() { + var forceExcessReference = new ExcessReferencePhase(); + phase = forceExcessReference; + + var methodName = "simple"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(MissingReferenceException.class); + } + + @Test + public void testPhiResolver() { + var phiResolver = new PhiResolver(); + phase = phiResolver; + + var methodName = "simple"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + Assert.assertNull(exception); + Assert.assertEquals(phiResolver.label.dests.curr[phiResolver.label.dests.count - 1], phiResolver.location); + } + + @Test + public void testConstRematerializer() { + var constRemPhase = new PhantomConstRematerializationPhase(); + phase = constRemPhase; + + var methodName = "simple"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(ConstantRematerializedToStackException.class); + var crException = (ConstantRematerializedToStackException) exception; + Assert.assertEquals(constRemPhase.stackSlotValue, crException.location); + Assert.assertEquals(constRemPhase.variableValue, crException.variable); + Assert.assertEquals(constRemPhase.constant, crException.state.getValue()); + } } // Taken from EnumSwitchTest, shortened From 689ffd02bf71c7c82acfbdbe495fe7cfe9122ef7 Mon Sep 17 00:00:00 2001 From: danocmx Date: Sat, 4 Apr 2026 19:19:19 +0200 Subject: [PATCH 102/112] Fix minor issues --- .../graal/compiler/core/test/RegAllocVerifierTest.java | 2 +- .../lir/alloc/verifier/FromUsageResolverGlobal.java | 6 ++---- .../jdk/graal/compiler/lir/alloc/verifier/RAValue.java | 9 ++++++--- .../compiler/lir/alloc/verifier/RegAllocVerifier.java | 2 +- .../lir/alloc/verifier/UnknownAllocationState.java | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index e834fe371076..b58f03149a76 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -821,7 +821,7 @@ class LoadConstOp extends LIRInstruction implements StandardOp.LoadConstantOp { @Def({REG, STACK}) protected AllocatableValue result; JavaConstant input; - public LoadConstOp(AllocatableValue result, JavaConstant input) { + LoadConstOp(AllocatableValue result, JavaConstant input) { super(TYPE); this.result = result; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index d8ca223a9c4b..deb2b307273d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -155,7 +155,6 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b this.firstUsages = new EconomicHashMap<>(); this.initialLocations = new EconomicHashMap<>(); this.aliasMap = new EconomicHashMap<>(); - this.aliasMap = new EconomicHashMap<>(); this.endBlocks = new EconomicHashSet<>(); this.blockUsageMap = new BlockMap<>(lir.getControlFlowGraph()); @@ -254,9 +253,8 @@ protected boolean mergeInto(BlockUsage block, BlockUsage successor) { continue; // Do not push already resolved variables further } - var defValue = successor.locations.get(variable); - - if (block.locations.containsKey(variable) && block.locations.get(variable) == defValue) { + RAValue defValue = successor.locations.get(variable); + if (block.locations.containsKey(variable) && block.locations.get(variable).equals(defValue)) { continue; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index 794bc0d3a0a0..136f638ed1a4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -117,12 +117,15 @@ public int hashCode() { @Override public boolean equals(Object other) { if (other instanceof RAValue otherValueWrap) { - if (LIRValueUtil.isVirtualStackSlot(this.value) && otherValueWrap.value.equals(this.value)) { + if (LIRValueUtil.isVirtualStackSlot(this.value) && LIRValueUtil.isVirtualStackSlot(otherValueWrap.value)) { return LIRValueUtil.asVirtualStackSlot(this.value).getId() == LIRValueUtil.asVirtualStackSlot(otherValueWrap.value).getId(); } - if (ValueUtil.isStackSlot(this.value) && otherValueWrap.value.equals(this.value)) { - return ValueUtil.asStackSlot(this.value).getRawOffset() == ValueUtil.asStackSlot(otherValueWrap.value).getRawOffset(); + if (ValueUtil.isStackSlot(this.value) && ValueUtil.isStackSlot(otherValueWrap.value)) { + var ourSlot = ValueUtil.asStackSlot(this.value); + var otherSlot = ValueUtil.asStackSlot(otherValueWrap.value); + + return ourSlot.getRawOffset() == otherSlot.getRawOffset() && ourSlot.getRawAddFrameSize() == otherSlot.getRawAddFrameSize(); } return this.value.equals(otherValueWrap.value); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 800b1e6344f3..85a1fea4bbf0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -181,7 +181,7 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { List exceptions = new ArrayList<>(); for (var blockId : this.lir.getBlocks()) { var block = this.lir.getBlockById(blockId); - var state = this.blockEntryStates.get(block); + var state = new BlockVerifierState(block, this.blockEntryStates.get(block)); var instructions = this.blockInstructions.get(block); for (var instr : instructions) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index d9ba9fbd4179..4cca36f6c517 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -59,7 +59,7 @@ public AllocationState meet(AllocationState other, BasicBlock otherBlock, Bas return newConfState; } - return new ConflictedAllocationState((ValueAllocationState) other, ValueAllocationState.createUndefined(null)); + return new ConflictedAllocationState((ValueAllocationState) other, ValueAllocationState.createUndefined(block)); } @Override From f81cca683378b314a969caec284f73afad04b460 Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 8 Apr 2026 10:41:32 +0200 Subject: [PATCH 103/112] Make references a set and remove worklist.remove(block) --- .../graal/compiler/core/test/RegAllocVerifierTest.java | 3 ++- .../lir/alloc/verifier/BlockVerifierState.java | 10 +--------- .../lir/alloc/verifier/FromUsageResolverGlobal.java | 4 +--- .../compiler/lir/alloc/verifier/RAVInstruction.java | 3 ++- .../compiler/lir/alloc/verifier/ReferencesBuilder.java | 2 +- .../compiler/lir/alloc/verifier/RegAllocVerifier.java | 1 - 6 files changed, 7 insertions(+), 16 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index b58f03149a76..375ea1acbbe1 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -1024,7 +1024,8 @@ protected BlockMap> getVerifierInstructions(LIR lir, M var kind = curr.getLIRKind().makeUnknownReference(); var refLocation = RAValue.create(curr.asRegister().getRegister().asValue(kind)); - op.references = List.of(refLocation); + op.references = new EconomicHashSet<>(); + op.references.add(refLocation); return instructions; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index ef0d603996e7..ec1ccacb4415 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -749,15 +749,7 @@ protected void updateWithSafePoint(RAVInstruction.Op op) { continue; // Not a reference, continue } - boolean referenceFound = false; - for (RAValue reference : op.references) { - if (reference.equals(entry.getKey())) { - referenceFound = true; - break; - } - } - - if (referenceFound) { + if (op.references.contains(entry.getKey())) { continue; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index deb2b307273d..17093e0b9463 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -233,7 +233,6 @@ protected void processBlock(Queue> worklist) { } } - worklist.remove(pred); worklist.add(pred); } } @@ -278,7 +277,7 @@ protected void initializeUsages() { // Calculate what is defined when + usages Set> visited = new EconomicHashSet<>(); while (!worklist.isEmpty()) { - var block = worklist.remove(); + var block = worklist.poll(); if (visited.contains(block)) { continue; } @@ -370,7 +369,6 @@ protected void initializeUsages() { for (int i = 0; i < block.getSuccessorCount(); i++) { var succ = block.getSuccessorAt(i); - worklist.remove(succ); worklist.add(succ); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 273217809b27..eab4b64bed1e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -38,6 +38,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Locale; +import java.util.Set; public class RAVInstruction { /** @@ -268,7 +269,7 @@ public static class Op extends Base { * List of GC roots, calculated using LocationMarker class, other references in state maps * need to be nullified. */ - public List references; + public Set references; /** * Count number of values stored. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java index b8cb99cefa48..60291fe06fb4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java @@ -164,7 +164,7 @@ protected void processState(LIRInstruction instruction, LIRFrameState info, Refe } var op = (RAVInstruction.Op) preAllocMap.get(instruction); - op.references = values.references.stream().toList(); + op.references = new EconomicHashSet<>(values.references); // Has to be a copy here } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 85a1fea4bbf0..2d9cf954361a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -140,7 +140,6 @@ public void calculateEntryBlocks() { } this.blockEntryStates.put(succ, succState); - worklist.remove(succ); // Always at the end, for predecessors to be processed first. worklist.add(succ); } } From 615f0c978073a6810c1544d72b92770d1659b765 Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 8 Apr 2026 10:42:15 +0200 Subject: [PATCH 104/112] Move ConflictResolver prepare stage --- .../compiler/lir/alloc/verifier/ConflictResolver.java | 10 ++++++++++ .../compiler/lir/alloc/verifier/RegAllocVerifier.java | 8 ++++++-- .../lir/alloc/verifier/VariableSynonymMap.java | 6 +++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index 7214999ba201..a312a26b734a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; @@ -46,6 +47,15 @@ public interface ConflictResolver { */ void prepare(LIR lir, BlockMap> blockInstructions); + /** + * This is run during the checking/verification stage, before this any + * conflict resolution is needed. + * + * @param instruction RAV instruction that we build conflict resolver information from + * @param block Block where this instruction is in + */ + void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block); + /** * Resolve an issue stemming from {@link ValueAllocationState} not having the correct value in * verification phase. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 2d9cf954361a..9d0154984509 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -161,6 +161,9 @@ public void verifyInstructionInputs() { var instructions = this.blockInstructions.get(block); for (var instr : instructions) { + this.constantMaterializationConflictResolver.prepareFromInstr(instr, block); + this.synonymMap.prepareFromInstr(instr, block); + state.check(instr); state.update(instr); } @@ -184,6 +187,9 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { var instructions = this.blockInstructions.get(block); for (var instr : instructions) { + this.constantMaterializationConflictResolver.prepareFromInstr(instr, block); + this.synonymMap.prepareFromInstr(instr, block); + try { state.check(instr); state.update(instr); @@ -223,8 +229,6 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { *

*/ public void run() { - this.constantMaterializationConflictResolver.prepare(lir, blockInstructions); - this.synonymMap.prepare(lir, blockInstructions); this.fromUsageResolverGlobal.resolvePhiFromUsage(); this.calculateEntryBlocks(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index 1d1905ff1291..7a9f00ec5c13 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -81,19 +81,19 @@ protected boolean isSynonymOf(RAVariable source, RAVariable target) { return find(source).equals(find(target)); } - @Override + // @Override public void prepare(LIR lir, BlockMap> blockInstructions) { for (var blockId : lir.getBlocks()) { var block = lir.getBlockById(blockId); var instructions = blockInstructions.get(block); for (var instruction : instructions) { - this.prepareFromInstr(instruction); + this.prepareFromInstr(instruction, block); } } } - public void prepareFromInstr(RAVInstruction.Base instruction) { + public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { if (instruction instanceof RAVInstruction.ValueMove move) { if (!move.variableOrConstant.isVariable() || !move.getLocation().isVariable()) { return; From 715d93b34415904d67b3b87ae4574bb4f7550631 Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 8 Apr 2026 10:42:41 +0200 Subject: [PATCH 105/112] Add test for deleted reference --- .../core/test/RegAllocVerifierTest.java | 99 ++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index 375ea1acbbe1..bcc169800a13 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -1135,6 +1135,59 @@ protected BlockMap> getVerifierInstructions(LIR lir, M } } + class DeleteReferencePhase extends RAVPhaseWrapper { + RAValue location; + RAValue variable; + + protected BlockMap> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { + var instructions = super.getVerifierInstructions(lir, instrMap, context); + + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructionsForBlock = instructions.get(block); + + RAVInstruction.Op prev = null; + for (var instruction : instructionsForBlock) { + if (instruction instanceof RAVInstruction.Op op) { + if (prev != null) { + if (setReferences(op.uses) || setReferences(op.alive)) { + prev.references = new EconomicHashSet<>(); + return instructions; + } + } + + if (!op.isLabel()) { + // Check fix, the previous instruction cannot define + // the reference being discarded, because then this phase + // won't work + prev = op; + } + } + } + } + + return instructions; + } + + protected boolean setReferences(RAVInstruction.ValueArrayPair values) { + for (int i = 0; i < values.count; i++) { + var orig = values.orig[i]; + var curr = values.curr[i]; + + if (orig.isIllegal() || curr == null || curr.isIllegal()) { + continue; + } + + if (!orig.getLIRKind().isValue()) { + location = curr; + variable = orig; + return true; + } + } + return false; + } + } + @Override protected LIRSuites createLIRSuites(OptionValues options) { if (validSuites) { @@ -1206,11 +1259,11 @@ protected void compileModified(String name) { } protected void assertException(Class expected) { + Assert.assertNotNull("No exception was thrown", exception); if (exception.getCause() != null) { exception = exception.getCause(); } - Assert.assertNotNull("No exception was thrown", exception); Assert.assertTrue("Unexpected exception: " + exception, expected.isInstance(exception)); } @@ -1299,6 +1352,32 @@ public static void loop(int a, int b) { } } + class A { + protected int a; + + A(int a) { + this.a = a; + } + + public void increment() { + this.a++; + } + + public int getA() { + return a; + } + } + + A obj; + + public int referenceSnippet(int a, int b) { + obj = new A(a); + while (obj.getA() < b) { + obj.increment(); + } + return obj.getA(); + } + @Before public void prepareTest() { exception = null; @@ -1606,6 +1685,24 @@ public void testConstRematerializer() { Assert.assertEquals(constRemPhase.variableValue, crException.variable); Assert.assertEquals(constRemPhase.constant, crException.state.getValue()); } + + @Test + public void testDeleteReference() { + var deleteReferencePhase = new DeleteReferencePhase(); + phase = deleteReferencePhase; + + var methodName = "referenceSnippet"; + compile(getResolvedJavaMethod(methodName), null); + + Assert.assertNull(exception); + + compileModified(methodName); + + assertException(ValueNotInRegisterException.class); + var vnrException = (ValueNotInRegisterException) exception; + Assert.assertEquals(vnrException.variable, deleteReferencePhase.variable); + Assert.assertEquals(vnrException.location, deleteReferencePhase.location); + } } // Taken from EnumSwitchTest, shortened From f95f9cfbe2688a6c316c5b837cf3f0d10b957bcf Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 8 Apr 2026 17:04:12 +0200 Subject: [PATCH 106/112] Fix access to overloaded instruction in exceptions --- .../lir/alloc/verifier/MissingReferenceException.java | 8 ++++---- .../lir/alloc/verifier/OperandFlagMismatchException.java | 1 + .../lir/alloc/verifier/ValueNotInRegisterException.java | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java index d6a3be1f7989..332b01e9b1d2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java @@ -30,14 +30,14 @@ public class MissingReferenceException extends RAVException { public RAValue reference; public AllocationState state; - public RAVInstruction.Op op; + public RAVInstruction.Op instruction; public BlockVerifierState blockVerifierState; - public MissingReferenceException(RAVInstruction.Op op, BasicBlock block, RAValue reference, AllocationState state, BlockVerifierState blockVerifierState) { - super(getMessage(reference, state), op, block); + public MissingReferenceException(RAVInstruction.Op instruction, BasicBlock block, RAValue reference, AllocationState state, BlockVerifierState blockVerifierState) { + super(getMessage(reference, state), instruction, block); this.reference = reference; this.state = state; - this.op = op; + this.instruction = instruction; this.blockVerifierState = new BlockVerifierState(block, blockVerifierState); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java index 2528a61c6893..cff94241c42f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java @@ -45,6 +45,7 @@ public OperandFlagMismatchException(RAVInstruction.Op op, BasicBlock block, V super(getErrorMessage(value, flags), op, block); this.value = value; this.flags = flags; + this.instruction = op; } static String getErrorMessage(Value value, EnumSet flags) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 128f3db03f41..6f97d086833f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -53,6 +53,7 @@ public ValueNotInRegisterException(RAVInstruction.Op instruction, BasicBlock this.location = location; this.state = state; this.blockVerifierState = new BlockVerifierState(block, blockVerifierState); + this.instruction = instruction; } static String getErrorMessage(RAValue variable, RAValue location, AllocationState state) { From 54e1cba037c85799f5faceabda23819929132c00 Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 8 Apr 2026 17:44:36 +0200 Subject: [PATCH 107/112] Add FailOnFirst option --- .../compiler/core/test/RegAllocVerifierTest.java | 11 +++++++++++ .../lir/alloc/verifier/ConflictResolver.java | 4 ++-- .../verifier/RAVFailedVerificationException.java | 7 +++---- .../lir/alloc/verifier/RegAllocVerifier.java | 16 ++++++++++------ .../alloc/verifier/RegAllocVerifierPhase.java | 6 +++++- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index bcc169800a13..76addca4f6ee 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -24,6 +24,8 @@ */ package jdk.graal.compiler.core.test; +import jdk.graal.compiler.code.CompilationResult; +import jdk.graal.compiler.core.common.CompilationIdentifier; import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; @@ -59,6 +61,7 @@ import jdk.graal.compiler.lir.phases.AllocationPhase; import jdk.graal.compiler.lir.phases.LIRPhase; import jdk.graal.compiler.lir.phases.LIRSuites; +import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; @@ -79,6 +82,7 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.PlatformKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; import org.junit.Assert; @@ -1188,6 +1192,13 @@ protected boolean setReferences(RAVInstruction.ValueArrayPair values) { } } + @Override + protected CompilationResult compile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, CompilationResult compilationResult, CompilationIdentifier compilationId, OptionValues options) { + // Test cases won't pass if FailOnFirst is false + var newOptions = new OptionValues(options, RegAllocVerifierPhase.Options.RAVFailOnFirst, true); + return super.compile(installedCodeOwner, graph, compilationResult, compilationId, newOptions); + } + @Override protected LIRSuites createLIRSuites(OptionValues options) { if (validSuites) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index a312a26b734a..5a3f95b1c77e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -48,8 +48,8 @@ public interface ConflictResolver { void prepare(LIR lir, BlockMap> blockInstructions); /** - * This is run during the checking/verification stage, before this any - * conflict resolution is needed. + * This is run during the checking/verification stage, before this any conflict resolution is + * needed. * * @param instruction RAV instruction that we build conflict resolver information from * @param block Block where this instruction is in diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java index c02ff88a7ccf..12f96537a3c1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -37,15 +37,14 @@ public class RAVFailedVerificationException extends GraalError { public List exceptions; - public RAVFailedVerificationException(String compUnitName, List exceptions) { - super(RAVFailedVerificationException.getMessage(compUnitName, exceptions)); + public RAVFailedVerificationException(List exceptions) { + super(RAVFailedVerificationException.getMessage(exceptions)); this.exceptions = exceptions; } - static String getMessage(String compUnitName, List exceptions) { + static String getMessage(List exceptions) { StringBuilder sb = new StringBuilder("Failed to verify "); - sb.append(compUnitName); sb.append(":"); for (var e : exceptions) { sb.append(" - "); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 9d0154984509..82cda9674d87 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -176,10 +176,8 @@ public void verifyInstructionInputs() { /** * Verify every instruction and collect every exception that has occurred. - * - * @param compUnitName Name of this compilation unit, we are verifying */ - public void verifyInstructionsAndCollectErrors(String compUnitName) { + public void verifyInstructionsAndCollectErrors() { List exceptions = new ArrayList<>(); for (var blockId : this.lir.getBlocks()) { var block = this.lir.getBlockById(blockId); @@ -208,7 +206,7 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { } if (!exceptions.isEmpty()) { - throw new RAVFailedVerificationException(compUnitName, exceptions); + throw new RAVFailedVerificationException(exceptions); } } @@ -228,11 +226,17 @@ public void verifyInstructionsAndCollectErrors(String compUnitName) { * location not being used as temp or output of same instruction. *

*/ - public void run() { + @SuppressWarnings("try") + public void run(boolean failOnFirst) { this.fromUsageResolverGlobal.resolvePhiFromUsage(); this.calculateEntryBlocks(); - this.verifyInstructionInputs(); + + if (failOnFirst) { + this.verifyInstructionInputs(); + } else { + this.verifyInstructionsAndCollectErrors(); + } } public VerifierPrinter getPrinter(OutputStream out) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 91c8e1877103..0ba4ff6441f5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -75,6 +75,8 @@ public static class Options { @Option(help = "Verify output of stack allocator with register allocator", type = OptionType.Debug) public static final OptionKey VerifyStackAllocator = new OptionKey<>(true); @Option(help = "Collect reference map information to verify", type = OptionType.Debug) public static final OptionKey CollectReferences = new OptionKey<>(true); + + @Option(help = "Fail on first verification failure", type = OptionType.Debug) public static final OptionKey RAVFailOnFirst = new OptionKey<>(true); } /** @@ -277,8 +279,10 @@ protected void verifyAllocation(LIR lir, Map Date: Wed, 8 Apr 2026 22:54:44 +0200 Subject: [PATCH 108/112] Run spellcheck for comments --- .../core/test/RegAllocVerifierTest.java | 50 +++++++-------- .../AliveConstraintViolationException.java | 2 +- .../lir/alloc/verifier/AllocationState.java | 8 +-- .../alloc/verifier/AllocationStateMap.java | 11 ++-- .../alloc/verifier/BlockVerifierState.java | 64 +++++++++---------- .../lir/alloc/verifier/CalleeSaveMap.java | 16 ++--- .../lir/alloc/verifier/ConflictResolver.java | 2 +- .../verifier/ConflictedAllocationState.java | 6 +- ...nstantMaterializationConflictResolver.java | 6 +- .../verifier/FromUsageResolverGlobal.java | 25 ++++---- .../verifier/KindsMismatchException.java | 2 +- .../lir/alloc/verifier/RARegister.java | 2 +- .../RAVFailedVerificationException.java | 4 +- .../lir/alloc/verifier/RAVInstruction.java | 28 ++++---- .../compiler/lir/alloc/verifier/RAValue.java | 9 +-- .../lir/alloc/verifier/ReferencesBuilder.java | 4 +- .../lir/alloc/verifier/RegAllocVerifier.java | 24 +++---- .../alloc/verifier/RegAllocVerifierPhase.java | 45 ++++++------- ...aterializedConstantSourceMissingError.java | 4 +- .../verifier/UnknownAllocationState.java | 4 +- .../alloc/verifier/ValueAllocationState.java | 5 +- .../verifier/ValueNotInRegisterException.java | 22 ++++++- .../alloc/verifier/VariableSynonymMap.java | 2 +- 23 files changed, 182 insertions(+), 163 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index 76addca4f6ee..035fb35665dc 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -105,7 +105,7 @@ public class RegAllocVerifierTest extends GraalCompilerTest { boolean validSuites = true; /** - * Exception thrown during verification process. + * Exception thrown during the verification process. */ RAVException exception; @@ -131,7 +131,7 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo /** * Overwrites a destination variable with a newly created one to cause a - * ValueNotInLocationException with old variable being in said place. + * ValueNotInLocationException with an old variable being in the said place. */ class ChangeVariablePhase extends RAVPhaseWrapper { protected RAVariable originalVariable; @@ -189,7 +189,7 @@ protected RAVariable createNewVariable(LIR lir, RAVariable oldVariable) { /** * This pass changes register allocation config to only allow certain registers to be used for - * allocation, and we want the verifier to detect usage of said register. + * allocation, and we want the verifier to detect usage of the said register. */ class DisallowedRegisterPhase extends RAVPhaseWrapper { protected Register ignoredReg; @@ -267,7 +267,7 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet saveInstructionsPreAlloc(LIR } /** - * Get block where a location with conflicted state will be used. + * Get a block where a location with a conflicted state will be used. * * @param lir LIR * @return Conflicted use block @@ -658,11 +658,11 @@ protected RAVariable getUsedVariableFromBlock(LIR lir, BasicBlock block, Map< } /** - * Adds a conflict inducing value move to a block. + * Adds a conflict-inducing value move to a block. * * @param lir LIR * @param block Block where we are putting move to - * @param instrMap Pre allocation instruction map + * @param instrMap Pre-allocation instruction map * @param targetVariable Target variable we are creating conflict with * @param variables Variables and their locations from previous steps */ @@ -1047,7 +1047,7 @@ protected BlockMap> getVerifierInstructions(LIR lir, M } // This instruction will define dest location - // next instruction will verify the references list + // next instruction will verify the reference list // -> no reference there! prev = op; } @@ -1320,7 +1320,7 @@ public static Object arrayLengthProviderSnippet(ArrayList list, boolean a) { } if (array[0] instanceof String || a) { /* - * This code is outside of the loop. Accessing the array reqires a ValueProxyNode. + * This code is outside of the loop. Accessing the array requires a ValueProxyNode. * When the simplification of the ArrayLengthNode replaces the length access with * the ArrayList.size used to create the array, then the value needs to have a * ValueProxyNode too. In addition, the two parts of the if-condition actually lead @@ -1474,9 +1474,9 @@ public void testConflictedLoc() { Assert.assertTrue(vnrException.state.isConflicted()); var confState = (ConflictedAllocationState) vnrException.state; - var conflitedStates = confState.getConflictedStates(); - Assert.assertEquals(2, conflitedStates.size()); - for (var state : conflitedStates) { + var conflictedStates = confState.getConflictedStates(); + Assert.assertEquals(2, conflictedStates.size()); + for (var state : conflictedStates) { Assert.assertTrue(state.getRAValue().equals(diamondConflictPhase.newVariable) || state.getRAValue().equals(diamondConflictPhase.targetVariable)); } } @@ -1498,9 +1498,9 @@ public void testConflictInLoop() { Assert.assertTrue(vnrException.state.isConflicted()); var confState = (ConflictedAllocationState) vnrException.state; - var conflitedStates = confState.getConflictedStates(); - Assert.assertEquals(2, conflitedStates.size()); - for (var state : conflitedStates) { + var conflictedStates = confState.getConflictedStates(); + Assert.assertEquals(2, conflictedStates.size()); + for (var state : conflictedStates) { Assert.assertTrue(state.getRAValue().equals(loopConflictPhase.newVariable) || state.getRAValue().equals(loopConflictPhase.targetVariable)); } } @@ -1522,9 +1522,9 @@ public void testConflictInInfiniteLoop() { Assert.assertTrue(vnrException.state.isConflicted()); var confState = (ConflictedAllocationState) vnrException.state; - var conflitedStates = confState.getConflictedStates(); - Assert.assertEquals(2, conflitedStates.size()); - for (var state : conflitedStates) { + var conflictedStates = confState.getConflictedStates(); + Assert.assertEquals(2, conflictedStates.size()); + for (var state : conflictedStates) { Assert.assertTrue(state.getRAValue().equals(loopConflictPhase.newVariable) || state.getRAValue().equals(loopConflictPhase.targetVariable)); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java index f6848f20fe55..284012c15fcf 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java @@ -27,7 +27,7 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; /** - * Violation of the alive inputs occurred, same location was marked as alive argument as well as + * Violation of the alive inputs occurred, the same location was marked as alive argument as well as * temp or output. */ @SuppressWarnings("serial") diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index 61fb89656278..0fa3fc16f751 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -67,12 +67,12 @@ public boolean isConflicted() { public abstract AllocationState clone(); /** - * Meet a state from different block coming from edge in the program graph, decide what result - * of said two states should be. + * Meet a state from a different block coming from an edge in the program graph, decide what the + * result of said two states should be. * - * @param other Other state coming from a predecessor edge + * @param other The other state coming from a predecessor edge * @param otherBlock Which block is other state from - * @param block Which state is this state from + * @param block Which state is this state from? * @return What is the new state the location is in. */ public abstract AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java index 48e575a0f6ce..4d2ebffde326 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java @@ -35,7 +35,7 @@ import java.util.Set; /** - * Mapping between a location and allocation state, that stores one of these: - + * Mapping between a location and allocation state that stores one of these: - * {@link UnknownAllocationState unknown} - our null state, nothing was stored yet - * {@link ValueAllocationState value} - symbol that is stored at said location - * {@link ConflictedAllocationState conflicted} - set of Values that are supposed to be at same @@ -113,7 +113,7 @@ public void put(RAValue key, AllocationState state, RAVInstruction.Base instruct * used. * *

- * This is useful for registers that are used by the ABI in the first label, but can actually + * This is useful for registers that are used by the ABI in the first label but can actually * never be changed, like rbp. *

* @@ -141,10 +141,10 @@ public void putClone(RAValue key, AllocationState state, RAVInstruction.Base ins } /** - * Get set of locations holding this particular variable/constant. + * Get the set of locations holding this particular variable/constant. * * @param value Symbol we are looking for - * @return Set of locations that hold said symbol + * @return Set of locations holding the symbol */ public Set getValueLocations(RAValue value) { Set locations = new EconomicHashSet<>(); @@ -159,8 +159,7 @@ public Set getValueLocations(RAValue value) { } /** - * Merge two maps together, source is generally the predecessor to the current block (this state - * map). + * Merge two maps, a source is generally the predecessor to the current block (this state map). * * @param source Predecessor merging to here * @return Was this map changed? diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index ec1ccacb4415..0a6b0dd7965f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -42,8 +42,8 @@ /** * Verification state a block is in, holds a mapping between locations and their allocation states, - * which can be unknown, value - it's contents or conflicted - multiple values conflict with each - * other. + * which can be unknown, value (with symbol content) or conflicted; multiple values conflict with + * each other. */ public class BlockVerifierState { /** @@ -95,7 +95,7 @@ public AllocationStateMap getValues() { } /** - * Merge states of block and it's predecessor. This process creates a new state based on + * Merge states of block and it's predecessor. This process creates a new state based on the * contents of the predecessor, creating conflicts where current locations do not match. * * @param other Predecessor of this block @@ -155,7 +155,7 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { if (curr == null) { if (op.isJump()) { // This can happen if a variable without a usage is passed in - // even when this variable acts as an alias to the next label + // even when this variable acts as an alias to the next label, // there's no usage, so no location. return; } @@ -177,8 +177,8 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { AllocationState state = this.values.get(curr); if (orig.equals(curr)) { // For these cases we do not consider checking state taking the original - // register as a symbol, because there's too many cases when this does - // not work, for example RETURN with rax tends to contain the actual + // register as a symbol, because there are too many cases when this does + // not work, for example, RETURN with rax tends to contain the actual // generated variable instead of rax symbol, or NEAR_FOREIGN_CALL // keeps its own registers before and after allocation, but those // can also contain different variable symbols. @@ -286,7 +286,7 @@ protected boolean kindsEqual(ValueKind origInputKind, ValueKind currInputK *

* * @param orig Original variable - * @param fromState Value stored in state of the current location + * @param fromState Value stored in the state of the current location * @return Are they equal? */ protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { @@ -349,7 +349,7 @@ protected void checkReferences(RAVInstruction.Op op) { } if (!valAllocState.getRAValue().getLIRKind().isValue()) { - continue; // Is a reference + continue; // State holds a reference } throw new MissingReferenceException(op, block, reference, state, this); @@ -364,7 +364,7 @@ protected void checkReferences(RAVInstruction.Op op) { var valAllocState = (ValueAllocationState) state; if (!valAllocState.getRAValue().getLIRKind().isValue()) { - continue; // Is a reference + continue; // State holds a reference } throw new MissingReferenceException(op, block, reference, state, this); @@ -407,8 +407,8 @@ protected void checkValueMoveKinds(RAVInstruction.ValueMove move) { } /** - * Does this move change the ValueKind? This happens for when derived reference gets changed to - * a normal reference, we make sure that the underlying platform kind is equal, and then allow + * Does this move change the ValueKind? This happens for when a derived reference gets changed + * to a normal reference, we make sure that the underlying platform kind is equal and then allow * change from derived ([.+]) to normal reference ([*]). * *

@@ -424,7 +424,7 @@ protected void checkValueMoveKinds(RAVInstruction.ValueMove move) { * JUMP ~outgoingValues: [v8|QWORD[.+] -> rdx|QWORD[*]] destination: B1 -> B3 isThreadedJump: false * * - * Add calculates the derived reference address, move casts it to LIRKind ref and value is used + * Add calculates the derived reference address, move casts it to LIRKind ref, and value is used * in the JUMP to the next block, where pre-allocation symbol (variable) has derived ref, but * location has reference. *

@@ -490,7 +490,7 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { throw new RAVException(orig + " -> " + curr + " not an object java kind when marked as a reference", op, block); } else { if (origLIRKind.isValue() && currLIRKind.isValue()) { - // Either not a reference, or a derived one - which might not be marked as + // Either not a reference or a derived one - which might not be marked as // Object continue; } @@ -553,12 +553,12 @@ protected void checkAliveConstraint(RAVInstruction.Op instruction) { } /** - * Make sure concrete current locations changed by the allocator are not violating set of + * Make sure concrete current locations changed by the allocator are not violating a set of * {@link jdk.graal.compiler.lir.LIRInstruction.OperandFlag flags}, which specify what type can * they be. This is done on every array of pairs (dest, uses, alive, temp). * * @param valuePairs Value array pair we are verifying - * @param op Instruction which holds this array, for tracing in exceptions + * @param op Instruction that holds this array, for tracing in exceptions * @throws OperandFlagMismatchException Operand is a wrong type based on OperandFlag set. */ protected void checkOperandFlags(RAVInstruction.ValueArrayPair valuePairs, RAVInstruction.Op op) { @@ -639,7 +639,7 @@ public void updateWithLocationMove(RAVInstruction.LocationMove move) { */ protected void updateWithOp(RAVInstruction.Op op) { if (op.references != null) { - // First we remove unknown references + // First we remove unknown references, // then we define new values by the return value updateWithSafePoint(op); } @@ -654,7 +654,7 @@ protected void updateWithOp(RAVInstruction.Op op) { for (int i = 0; i < op.dests.count; i++) { if (op.dests.orig[i].isIllegal()) { - continue; // Safe to ignore, when destination is illegal value, not when used. + continue; // Safe to ignore when destination is illegal value, not when used. } assert op.dests.orig[i] != null; @@ -669,8 +669,8 @@ protected void updateWithOp(RAVInstruction.Op op) { RAValue variable = op.dests.orig[i]; if (location.equals(variable)) { - // Only check register validity if it was changed by the register allocator - // for example: rbp is used as input to start block and forbidden to be used by the + // Only check register validity if it was changed by the register allocator, + // for example, rbp is used as input to start block and forbidden to be used by the // allocator this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op, block)); } else { @@ -727,9 +727,9 @@ protected RAVInstruction.LocationMove castMove(RAVInstruction.Op op) { } /** - * Update block state with a safe point list of live references deemed by the GC, any other - * references not included in said list are to be set as unknown so there's no freed pointer - * use. + * Update the block state with a safe point list of live references deemed by the GC; any other + * references not included in the said list are to be set as unknown, so there's no freed + * pointer use. * *

* References need to be retrieved using {@link LocationMarker} classes. @@ -753,17 +753,17 @@ protected void updateWithSafePoint(RAVInstruction.Op op) { continue; } - // Remove all references that are not present in the references list, + // Remove all references that are not present in the reference list; // maybe it makes sense to keep registers that have live references, - // that are same as the one in references list? Because said list - // is expected to have stack slots and registers can retain same references. + // that are same as the one in the reference list? Because the list + // is expected to have stack slots and registers can retain the same references. entry.setValue(new ValueAllocationState(new RAValue(Value.ILLEGAL), op, block)); } } /** - * Take list of callee saved registers and add them to the start block state with their own - * values as symbols in order to check that they were correctly retrieved at exit point. + * Take the list of callee saved registers and add them to the start block state with their own + * values as symbols in order to check that they were correctly retrieved at an exit point. */ protected void updateCalleeSavedRegisters() { var registers = this.registerAllocationConfig.getRegisterConfig().getCalleeSaveRegisters(); @@ -776,7 +776,7 @@ protected void updateCalleeSavedRegisters() { var presentState = values.get(regValue); if (presentState instanceof ValueAllocationState valueAllocationState) { - // Keep the old value from the label, but save it for check at exit point. + // Keep the old value from the label but save it for check at exit point. if (!valueAllocationState.getRAValue().equals(regValue)) { // If the symbol is the same register, then override it with CalleeSaveRegister calleeSaveMap.addValue(regValue, valueAllocationState.getRAValue()); @@ -813,7 +813,7 @@ protected void checkCalleeSavedRegisters() { var stateValue = valueAllocationState.getRAValue(); var calleeSavedValue = calleeSaveMap.getCalleeSavedValue(regValue); if (stateValue.equals(calleeSavedValue) && stateValue.getLIRKind().equals(calleeSavedValue.getLIRKind())) { - // Same symbol as register means the value was retrieved safely + // The same symbol as register means the value was retrieved safely. // Kinds also need to match continue; } @@ -824,8 +824,8 @@ protected void checkCalleeSavedRegisters() { } /** - * Update state with a {@link RAVInstruction.ValueMove}, if locations is concrete, we set it to - * a variable/constant, if it's a variable to variable move, then all locations containing old + * Update state with a {@link RAVInstruction.ValueMove}, if location is concrete, we set it to a + * variable/constant, if it's a variable to variable move, then all locations containing old * variable need to be changed to the new variable. * * @param valueMove move we update state from @@ -846,7 +846,7 @@ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove) { var value = valueAllocationState.getRAValue(); if (value instanceof CalleeSaveMap.CalleeSavedRegister) { // Virtual move in form r1 = VIRTMOVE v1, assigns variable - // v1 to callee saved register, this needs to be saved + // v1 to callee saved register; this needs to be saved // to properly check that callee saved value is retrieved // at exit point. calleeSaveMap.addValue(regLoc, valueMove.variableOrConstant.asVariable()); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java index db0151f9c0d3..055f51802961 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java @@ -33,11 +33,11 @@ import jdk.vm.ci.code.RegisterValue; /** - * Make sure callee-saved register values are retrieved at exit block. + * Make sure callee-saved register values are retrieved at an exit block. * *

- * Virtual moves assigning to same registers need to also be handled, because both values could be - * retrieved. + * Virtual moves assigning to the same registers need to also be handled, because both values could + * be retrieved. *

*/ public class CalleeSaveMap { @@ -64,8 +64,8 @@ public String toString() { } /** - * Create a callee saved register, this is signalling that this value needs to be retrieved on - * exit point, not just same register value. + * Create a callee saved register this is signaling that this value needs to be retrieved on + * exit point, not just the same register value. * * @param registerValue Callee saved register * @return Instance of callee saved register @@ -77,8 +77,8 @@ public CalleeSavedRegister createCalleeSavedRegister(RegisterValue registerValue } /** - * Add a variable from virtual move that is assigned to a callee saved register and can also be - * retrieved on exit. + * Add a variable from a virtual move that is assigned to a callee saved register and can also + * be retrieved on exit. * * @param register Callee saved register * @param value New callee saved value @@ -118,7 +118,7 @@ public boolean isCalleeSaveRegister(RARegister register) { } /** - * Get list of callee saved registers. + * Get the list of callee saved registers. * * @return callee saved registers */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java index 5a3f95b1c77e..948eb9f80937 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java @@ -31,7 +31,7 @@ import java.util.List; /** - * Resolve {@link ConflictedAllocationState} occurrences based on internal set of rules. + * Resolve {@link ConflictedAllocationState} occurrences based on an internal set of rules. * *

* In-case comparison of {@link ValueAllocationState} fails, it also might get resolved by this. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index 541fff7d0831..f09cacf72532 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -30,7 +30,7 @@ import java.util.Set; /** - * Conflicted allocation state - two or more instances have collided and either of them can be + * Conflicted allocation state - two or more instances have collided, and either of them can be * stored at said location, needs to be resolved by either overwriting the location with a new * {@link ValueAllocationState instance} or by a {@link ConflictResolver} implementation. */ @@ -88,7 +88,7 @@ public boolean isConflicted() { * Any state coming here will be added to the conflict set and create a new * {@link ConflictedAllocationState} instance. * - * @param other Other state coming from a predecessor edge + * @param other The other state coming from a predecessor edge * @return {@link ConflictedAllocationState} with predecessor state added up */ @Override @@ -123,7 +123,7 @@ public AllocationState clone() { /** * We do not compare a conflicted state on its contents, whenever a new one would be created as - * a result, the set of contents would remain the same, if values are equal based on RAValue + * a result, the set of contents would remain the same if values are equal based on RAValue * rules. * * @param other Another state we are comparing it to diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index fa7c080e8374..a6aa45ce8b9b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -74,7 +74,7 @@ public void prepare(LIR lir, BlockMap> blockInstructio } /** - * Add variable to constant mapping from instruction contents. + * Add a variable to constant mapping from instruction contents. * * @param instruction Instruction we are looking at for LoadConstantOp * @param block Block where instruction is from @@ -201,12 +201,12 @@ public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocati * source, stored in {@link ValueAllocationState}. * *

- * Check if variable cannot rematerialize to stack and if it did so. + * Check if the variable cannot rematerialize to stack and if it did so. *

* * @param variable Target variable * @param state {@link AllocationState state} it is in - * @return Was it rematerialized to wrong location? + * @return Was it rematerialized to a wrong location? */ protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllocationState state) { if (canRematerializeToStack.contains(variable)) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 17093e0b9463..0b87c6bd676f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -39,8 +39,8 @@ import java.util.Set; /** - * Resolve variables phi variables back to labels and jumps by find their first usage and handling - * any reg allocator inserted moves back to the defining label. + * Resolve label phi variables back to labels and jumps by find their first usage and handling any + * reg allocator inserted moves back to the defining label. * *

* Register allocator strips us of this information that is necessary for the verification. In order @@ -105,12 +105,12 @@ class AliasPair { /** * Map of variables are aliases for a list of variables used in predecessor jump instructions. - * First the successor label variable needs to be resolved and after the predecessor labels. + * First, the successor label variable needs to be resolved and after the predecessor labels. */ private Map> aliasMap; /** - * Block map of their usages objects. + * Block map of their usage objects. */ protected BlockMap blockUsageMap; @@ -120,7 +120,7 @@ class AliasPair { public Set> endBlocks; /** - * Information about locations of variables found when traversing LIR, from first usage, + * Information about locations of variables found when traversing LIR, from the first usage, * handling all related moves up until the label instruction that defined the variable. */ protected final class BlockUsage { @@ -162,8 +162,9 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b /** * Resolves label variable locations by finding where they are first used. Walk back from their - * usage to their defining label (bottom-up), handling any spills, reloads and moves along the - * way to set the location in label back after register allocator strips this information. + * usage to their defining label (bottom-up), handling any spills, reloads, and moves along the + * way to set the location in the label back after the register allocator strips this + * information. */ public void resolvePhiFromUsage() { Queue> worklist = new ArrayDeque<>(); @@ -293,7 +294,7 @@ protected void initializeUsages() { // TestCase: TruffleSafepointTest // java.concurrent.ForkJoinPool // some methods for this class have location kept in - // them after the register allocation is complete + // them after the register allocation is complete, // but such information should be stripped by the allocator. // This information uses one register for 2 variables in a label // and triggers an error in the verification @@ -352,7 +353,7 @@ protected void initializeUsages() { // Here we handle loops without any exit that might // also need a resolution of label variables, but // are not reachable from endBlocks set, so we - // add predecessors of such loops, that are part of the loop + // add predecessors of such loops that are part of the loop // into the endBlocks set to process them. var loop = block.getLoop(); if (loop.getNaturalExits().isEmpty() && loop.getLoopExits().isEmpty()) { @@ -432,10 +433,10 @@ protected void handleMove(BlockUsage usage, RAValue from, RAValue to) { } /** - * Resolve locations for all variables in a label, also mark first usage for aliased variables - - * variables used in predecessors that have no other usage. + * Resolve locations for all variables in a label, also mark the first usage for aliased + * variables - variables used in predecessors that have no other usage. * - * @param usage usage for the block we are resolving, contains the locations + * @param usage usage for the block we are resolving contains the locations * @param label label we are resolving * @param block block of the label */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java index 9422b8f5cbc7..fb352bdb75dd 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java @@ -42,7 +42,7 @@ public class KindsMismatchException extends RAVException { * @param block Block where violation occurred * @param value1 First value in comparison, original variable. * @param value2 Second value in comparison, either current location or value stored in state - * @param origVsCurr Comparing original variable to current location + * @param origVsCurr Comparing the original variable to the current location */ public KindsMismatchException(RAVInstruction.Base instruction, BasicBlock block, RAValue value1, RAValue value2, boolean origVsCurr) { super(KindsMismatchException.getErrorMessage(value1, value2, origVsCurr), instruction, block); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java index 37bcd08db0f5..e0e6a62078fb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java @@ -63,7 +63,7 @@ public int hashCode() { } /** - * Equal RegisterValue on it's Register, not Register and kind, otherwise same as Value. + * Equal RegisterValue on it's Register, not Register and kind, otherwise the same as a Value. * * @param other The reference object with which to compare. * @return Are said values equal? diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java index 12f96537a3c1..da79b0d16cc1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java @@ -30,8 +30,8 @@ /** * Composite exception taking every {@link RAVException exception} that occurred (exceptions caused - * by the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase}) and combining them together - * to one exception. + * by the {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase}) and combining them to one + * exception. */ @SuppressWarnings("serial") public class RAVFailedVerificationException extends GraalError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index eab4b64bed1e..d34296c68915 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -53,20 +53,24 @@ public abstract static class Base { /** * List of virtual moves to be inserted after this instruction, virtual moves are ones * removed by the allocator still holding relevant information to the verification process, - * for example first label always uses registers (instead of variables) based on ABI, and a - * move is inserted indicating that those registers have certain variables. + * for example, the first label always uses registers (instead of variables) based on ABI, + * and a move is inserted indicating that those registers have certain variables. * - * @example [rsi, rbp] = LABEL v1 = MOVE rsi + *

+         * {@code [rsi, rbp] = LABEL v1 = MOVE rsi}
+         * 
*/ protected List virtualMoveList; /** - * List of speculative moves, these might be removed, but still hold important information - * for us, so we add them to the verifier IR in-case they are, this happens when a MOVE + * List of speculative moves, these might be removed but still hold important information + * for us, so we add them to the verifier IR in case they are, this happens when a MOVE * source and target locations are equal (and thus redundant) but before allocation their * variable counter-parts are not equal. * - * @example before alloc: v1 = MOVE v2 after alloc: rax = MOVE rax + *

+ * Before alloc: {@code v1 = MOVE v2} after alloc: {@code rax = MOVE rax} + *

*/ protected List speculativeMoveList; @@ -178,7 +182,7 @@ public void addOrig(int index, Value value) { } /** - * Verify that all presumed values are present and that both sides have it. + * Verify that all presumed values are present and that both sides have them. * * @return true, if contents have been successfully verified, false if there's null in * either array. @@ -255,8 +259,8 @@ public static class Op extends Base { public ValueArrayPair alive; /** - * Pairs of values retrieved from LIRFrameState, verified same as any other input to make - * sure GC has all necessary information. + * Pairs of values retrieved from LIRFrameState, verified the same as any other input to + * make sure GC has all necessary information. */ public ValueArrayPair stateValues; @@ -272,7 +276,7 @@ public static class Op extends Base { public Set references; /** - * Count number of values stored. + * Count the number of values stored. */ private final class GetCountProcedure implements InstructionValueProcedure { private int valueCount = 0; @@ -444,7 +448,7 @@ public String toString() { } /** - * Reload symbol from stack slot to a register. + * Reload a symbol from a stack slot to a register. */ public static class Reload extends LocationMove { public RAValue from; @@ -518,7 +522,7 @@ public RAValue getLocation() { /** * Virtual move in from: v28|DWORD = MOVE input: rax|BYTE moveKind: DWORD, where the destination - * is a variable. We flip the relation so that the input register actually stores said + * is a variable. We flip the relation so that the input register actually stores the * symbol/variable, which keeps necessary verification information present. */ public static class VirtualLocationMove extends ValueMove { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index 136f638ed1a4..f10c17ae8ba6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -36,7 +36,7 @@ *

* Values are indexed without their {@link LIRKind kind} associated with them, this is necessary for * {@link AllocationStateMap} because locations can change kinds and still be associated with one - * key/value pair in said map. + * key/value pair in the map. *

*/ public class RAValue { @@ -107,9 +107,10 @@ public int hashCode() { } /** - * Are two {@link RAValue values} equal? - check for offset for {@link jdk.vm.ci.code.StackSlot} - * - check for id for {@link jdk.graal.compiler.lir.VirtualStackSlot} - otherwise default to - * normal equals on {@link Value} + * Are two {@link RAValue values} equal? - check for offset for + * {@link jdk.vm.ci.code.StackSlot}, check for id for + * {@link jdk.graal.compiler.lir.VirtualStackSlot}, otherwise default to normal equals on + * {@link Value} * * @param other The reference object with which to compare. * @return Are said values equal? diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java index 60291fe06fb4..d3c15e1293b6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java @@ -46,7 +46,7 @@ /** * Build references list for operations that can be used to in-validate references that are not part - * of it, to make sure GC-freed references are not used further. + * of it to make sure GC-freed references are not used further. * *

* It uses the {@link LocationMarker} class that is used in the @@ -151,7 +151,7 @@ private RegisterAttributes attributes(Register reg) { } /** - * Set references to the operation, if it can be found in the pre-allocation map. + * Set references to the operation if it can be found in the pre-allocation map. * * @param instruction LIR instruction that holds these references * @param info LIR frame state diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index 82cda9674d87..e3d0d2bf4f86 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -37,8 +37,8 @@ /** * Class encapsulating the whole Register Allocation Verification. Maintaining entry states for - * blocks, resolving label variable locations and checking validity of every location to variable - * correspondence. + * blocks, resolving label variable locations, and checking the validity of every location to + * variable correspondence. */ public class RegAllocVerifier { /** @@ -57,7 +57,7 @@ public class RegAllocVerifier { protected LIR lir; /** - * Register Allocator config used for validating if valid register is used by the allocator. + * Register Allocator config used for validating if the allocator uses a valid register. */ protected RegisterAllocationConfig registerAllocationConfig; @@ -100,8 +100,8 @@ public RegAllocVerifier(LIR lir, BlockMap> blockInstru } /** - * For every block, we need to calculate its entry state which is a combination of states of - * blocks that are its predecessors, we get after reached a fixed point state, where no entry + * For every block, we need to calculate its entry state, which is a combination of states of + * blocks that are its predecessors; we get after reached a fixed point state, where no entry * state is changed. * *

@@ -150,9 +150,9 @@ protected BlockVerifierState createNewBlockState(BasicBlock block) { } /** - * By using the entry states calculated in step beforehand, we check input of every instruction - * to see that it matches symbols before allocation, after wards we update the state so the next - * instruction has correct state at said instruction input. + * By using the entry states calculated in a step beforehand, we check the input of every + * instruction to see that it matches symbols before allocation; after wards we update the state + * so the next instruction has the correct state at said instruction input. */ public void verifyInstructionInputs() { for (var blockId : this.lir.getBlocks()) { @@ -216,14 +216,14 @@ public void verifyInstructionsAndCollectErrors() { * verify inputs of instructions match variables present before allocation. * *

- * The issues we are looking to catch are mostly about making sure that order of spills, reloads - * and moves is correct and that used location after stores the symbol that is supposed to be - * there. + * The issues we are looking to catch are mostly about making sure that the order of spills, + * reloads, and moves is correct and that used location after stores the symbol that is supposed + * to be there. *

* *

* We also make sure that kinds are still matching, operand flags aren't violated, alive - * location not being used as temp or output of same instruction. + * location not being used as temp or output of the same instruction. *

*/ @SuppressWarnings("try") diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 0ba4ff6441f5..3ba702fbc96a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -61,11 +61,11 @@ /** * Verification phase for Register Allocation, wraps around the actual allocator and validates that - * order of spills, reloads and moves is correct and that variables before allocation are actually - * stored in current locations chosen by the allocator. + * the order of spills, reloads, and moves is correct and that variables before allocation are + * actually stored in current locations chosen by the allocator. * *

- * Needs to extend RegisterAllocationPhase to not throw an exception. + * Needs to extend the RegisterAllocationPhase to not throw an exception. *

*/ public class RegAllocVerifierPhase extends RegisterAllocationPhase { @@ -108,7 +108,7 @@ public boolean getNeverSpillConstants() { } /** - * Get allocator that is being verified. + * Get the allocator that is being verified. * * @return Register allocator */ @@ -193,9 +193,10 @@ protected Map saveInstructionsPreAlloc(LIR if (this.isVirtualMove(instruction)) { // Virtual moves (variable = MOV real register) are going to be removed by the // allocator, but we still need the information about which variables are - // associated to which real registers, and so we store them. - // They are generally associated to other instructions - // that's why we append them here to the previous instruction (for example Label + // associated with real registers, and so we store them. + // They are generally associated with other instructions + // that's why we append them here to the previous instruction (for example, + // Label // or Foreign Call) use these, if this instruction was deleted in the allocator, // then they will be missing too. assert previousInstr != null; @@ -215,9 +216,9 @@ protected Map saveInstructionsPreAlloc(LIR if (this.isSpeculativeMove(instruction)) { speculative = true; // Speculative moves are in form ry = MOVE vx, which could be removed if - // variable ends up being allocated to the same register as ry. - // If it was removed we need to re-add it because it holds - // important information about where value of this variable + // the variable ends up being allocated to the same register as ry. + // If it was removed, we need to re-add it because it holds + // important information about where the value of this variable // is placed - for label resolution after the label. assert previousInstr != null; @@ -269,7 +270,7 @@ public void doState(LIRFrameState state) { } /** - * Use information before allocation to verify output of allocator(s). + * Use information before allocation to verify the output of allocator(s). * * @param lir LIR * @param preallocMap Map of instructions before allocation @@ -317,7 +318,7 @@ protected void verifyAllocation(LIR lir, Map preprocessAllocatedInstructions(LIR lir, Map handleSpeculativeMoves(RAVInstruction.Op op, Set presentInstructions, Map definedVariables) { List toAdd = new ArrayList<>(); @@ -516,7 +517,7 @@ protected List handleSpeculativeMoves(RAVInstruction.O /** * Create Register Verifier Instruction that was created by the {@link RegisterAllocationPhase - * register allocator}. Generally speaking, it's always a move instruction, other ones return + * register allocator}. Generally speaking, it's always a move instruction; other ones return * null. * * @param instruction LIR Instruction newly created by {@link RegisterAllocationPhase register @@ -587,8 +588,8 @@ protected boolean isVirtualMove(LIRInstruction instruction) { * be removed, but hold important information to the verification process. * *

- * For example, this happens for a move between two variables and after allocation locations are - * equal, making the move redundant. + * For example, this happens for a move between two variables, and after allocation locations + * are equal, making the move redundant. *

* * @param instruction {@link LIRInstruction instruction} we are looking at diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java index e13e56788cde..2ce49d11b01f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java @@ -25,8 +25,8 @@ package jdk.graal.compiler.lir.alloc.verifier; /** - * Re-materialized constant has wrong source (not a {@link RAVInstruction.ValueMove}), but either - * undefined or something different. + * Re-materialized constant has a wrong source (not a {@link RAVInstruction.ValueMove}), but either + * undefined or some different Operation or Move. */ @SuppressWarnings("serial") public class RematerializedConstantSourceMissingError extends RAVError { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index 4cca36f6c517..c21df03c3b41 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -41,10 +41,10 @@ public boolean isUnknown() { } /** - * Meet state from predecessor, if both are unknown then unknown is returned, otherwise + * Meet state from a predecessor, if both are unknown then unknown is returned, otherwise * {@link ConflictedAllocationState conflict} occurs. * - * @param other Other state coming from a predecessor edge + * @param other The other state coming from a predecessor edge * @return {@link UnknownAllocationState Unknown} if both are, otherwise a conflict */ @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index b98d337d7ca4..4e8f4a2baceb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -48,9 +48,6 @@ public ValueAllocationState(RAValue raValue, RAVInstruction.Base source, BasicBl // StackSlot, RegisterValue is present in start block in label as predefined argument // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS // ConstantValue act as Variable - - // We use variables as symbols for register validation - // but real registers can also be used as that, in some cases. this.value = raValue; this.source = source; this.block = block; @@ -97,7 +94,7 @@ public BasicBlock getBlock() { * equal, then same state is returned, otherwise a {@link ConflictedAllocationState conflict} is * created between said states. * - * @param other Other state coming from a predecessor edge + * @param other The other state coming from a predecessor edge * @param otherBlock Where the other state is coming from * @param currBlock Where the current state is coming from * @return {@link ValueAllocationState} if their contents are equal, otherwise diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java index 6f97d086833f..dae5540a0d3b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java @@ -32,8 +32,24 @@ @SuppressWarnings("serial") public class ValueNotInRegisterException extends RAVException { public RAVInstruction.Op instruction; - public RAValue variable; // Can be a constant or other symbolic value - public RAValue location; // Can be StackSlot, RegisterValue or memory + + /** + * Symbol that was not found at the location. + * + *

+ * Can be a constant, variable, or other symbolic value. + *

+ */ + public RAValue variable; + + /** + * Location where the symbol was not found. + * + *

+ * Can be a register or a (virtual) stack slot. + *

+ */ + public RAValue location; public AllocationState state; public BlockVerifierState blockVerifierState; @@ -42,7 +58,7 @@ public class ValueNotInRegisterException extends RAVException { * * @param instruction Instruction where violation occurred * @param block Block where violation occurred - * @param variable Target varible we are looking for + * @param variable Target variable we are looking for * @param location Location where we couldn't find it * @param state The actual state that the location is in */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index 7a9f00ec5c13..aed1566d60a9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -133,7 +133,7 @@ public ValueAllocationState resolveConflictedState(RAVariable target, Conflicted return null; } - // Currently take any source, but maybe its better to track the original variable + // Currently take any source, but maybe it is better to track the original variable source = valueState.source; block = valueState.block; } From bbb39418f71647d6101c3247b7e5aa3c8adf0e02 Mon Sep 17 00:00:00 2001 From: danocmx Date: Fri, 10 Apr 2026 17:15:08 +0200 Subject: [PATCH 109/112] Adjust debuf file output --- .../lir/alloc/verifier/RAVInstruction.java | 14 ++--- .../lir/alloc/verifier/VerifierPrinter.java | 56 +++++++++++++++++-- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index d34296c68915..2e2e956fea69 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -394,7 +394,7 @@ public LocationMove(LIRInstruction instr, Value from, Value to, boolean validate @Override public String toString() { - return to.toString() + " = MOVE " + from.toString(); + return to.getValue().toString() + " = MOVE " + from.getValue().toString(); } } @@ -413,7 +413,7 @@ public RegMove(LIRInstruction instr, RegisterValue from, RegisterValue to) { @Override public String toString() { - return to.toString() + " = REGMOVE " + from.toString(); + return to.getValue().toString() + " = REGMOVE " + from.getValue().toString(); } } @@ -443,7 +443,7 @@ public StackMove(LIRInstruction instr, Value from, Value to, Value backupSlot) { @Override public String toString() { - return to.toString() + " = STACKMOVE " + from.toString(); + return to.getValue().toString() + " = STACKMOVE " + from.getValue().toString(); } } @@ -462,7 +462,7 @@ public Reload(LIRInstruction instr, RegisterValue to, Value from) { @Override public String toString() { - return to.toString() + " = RELOAD " + from.toString(); + return to.getValue().toString() + " = RELOAD " + from.getValue().toString(); } } @@ -481,7 +481,7 @@ public Spill(LIRInstruction instr, Value to, RegisterValue from) { @Override public String toString() { - return to.toString() + " = SPILL " + from.toString(); + return to.getValue().toString() + " = SPILL " + from.getValue().toString(); } } @@ -505,7 +505,7 @@ public ValueMove(LIRInstruction instr, Value variableOrConstant, Value location) @Override public String toString() { - return getLocation().toString() + " = VALUEMOVE " + variableOrConstant.toString(); + return getLocation().getValue().toString() + " = VALUEMOVE " + variableOrConstant.getValue().toString(); } public void setLocation(RAValue location) { @@ -532,7 +532,7 @@ public VirtualLocationMove(LIRInstruction instr, Value variableOrConstant, Value @Override public String toString() { - return getLocation().toString() + " = VIRTMOVE " + variableOrConstant.toString(); + return getLocation().getValue().toString() + " = VIRTMOVE " + variableOrConstant.getValue().toString(); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index df774a59d0f0..20ed018ccc72 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -144,9 +144,13 @@ protected void printBlockHeader(BasicBlock block) { protected void printEntryState(BasicBlock block) { var blockVerifierState = verifier.blockEntryStates.get(block); + if (blockVerifierState == null) { + // If error occurred during creation of entry states, it might not be defined. + return; + } if (block.getId() == 0) { - return; + return; // Start block is always empty } out.println("Entry state:"); @@ -156,13 +160,53 @@ protected void printEntryState(BasicBlock block) { if (state.isUnknown()) { continue; } - out.println(location + " -> " + state); + + printAllocationState(location, state); } out.adjustIndentation(-INDENT); out.println(); } protected void printAllocationState(RAValue location, AllocationState state) { + String stateStr = switch (state) { + case ValueAllocationState st -> { + if (st.isUndefinedFromBlock()) { + yield "Value unknown from " + st.block; + } else { + yield "Value {" + st.getValue() + "} from " + st.source + " in " + st.block; + } + } + case ConflictedAllocationState st -> { + StringBuilder str = new StringBuilder(); + str.append("Conflicted {\n"); + for (var valueAllocState : st.getConflictedStates()) { + if (valueAllocState.isUndefinedFromBlock()) { + str.append("Value unknown from ").append(valueAllocState.block); + continue; + } else { + str.append(valueAllocState.getValue()); + if (valueAllocState.block != null) { + str.append(" from ").append(valueAllocState.block); + } + } + + str.append(", "); + } + + if (!st.getConflictedStates().isEmpty()) { + str.setLength(str.length() - 2); + } + + yield str.append("}").toString(); + } + case UnknownAllocationState st -> "Unknown"; + default -> throw new RAVError("Unexpected value: " + state); + }; + + out.println(location + " -> " + stateStr); + } + + protected void printAllocationStateInDetail(RAValue location, AllocationState state) { String stateStr = switch (state) { case ValueAllocationState st -> { if (st.isUndefinedFromBlock()) { @@ -177,7 +221,7 @@ protected void printAllocationState(RAValue location, AllocationState state) { str.append("Conflicted: \n"); for (var valueAllocState : st.getConflictedStates()) { if (valueAllocState.isUndefinedFromBlock()) { - str.append(" - Value unknown from ").append(valueAllocState.block); + str.append(" - Value unknown from ").append(valueAllocState.block).append('\n'); continue; } @@ -333,7 +377,7 @@ private void printOtherStates(ValueNotInRegisterException exception) { out.adjustIndentation(INDENT); for (var location : locations) { var state = exception.blockVerifierState.values.get(location); - printAllocationState(location, state); + printAllocationStateInDetail(location, state); } out.adjustIndentation(-INDENT); } @@ -345,7 +389,7 @@ private void printCalleeSavedValues(CalleeSavedRegisterNotRetrievedException exc for (var reg : registers) { var regValue = new RARegister(reg.asValue()); var state = exception.blockVerifierState.values.get(regValue); - printAllocationState(regValue, state); + printAllocationStateInDetail(regValue, state); } out.adjustIndentation(-INDENT); } @@ -365,7 +409,7 @@ private void printOtherReferences(MissingReferenceException exception) { } // Print all available references. - printAllocationState(location, state); + printAllocationStateInDetail(location, state); } out.adjustIndentation(-INDENT); } From cc4dbe1acf4c7b1401ad6cd9abb2428fc3083f1e Mon Sep 17 00:00:00 2001 From: danocmx Date: Fri, 10 Apr 2026 17:40:26 +0200 Subject: [PATCH 110/112] Add missing Override --- .../verifier/ConstantMaterializationConflictResolver.java | 1 + .../graal/compiler/lir/alloc/verifier/VariableSynonymMap.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java index a6aa45ce8b9b..dbf23cc75b38 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java @@ -79,6 +79,7 @@ public void prepare(LIR lir, BlockMap> blockInstructio * @param instruction Instruction we are looking at for LoadConstantOp * @param block Block where instruction is from */ + @Override public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index aed1566d60a9..3a127a5e6c37 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -81,7 +81,7 @@ protected boolean isSynonymOf(RAVariable source, RAVariable target) { return find(source).equals(find(target)); } - // @Override + @Override public void prepare(LIR lir, BlockMap> blockInstructions) { for (var blockId : lir.getBlocks()) { var block = lir.getBlockById(blockId); @@ -93,6 +93,7 @@ public void prepare(LIR lir, BlockMap> blockInstructio } } + @Override public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { if (instruction instanceof RAVInstruction.ValueMove move) { if (!move.variableOrConstant.isVariable() || !move.getLocation().isVariable()) { From 219c6c133e3b8e217ab074aaca054fb5fda37ec6 Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 29 Apr 2026 14:08:32 +0200 Subject: [PATCH 111/112] Changes based on review --- .../core/test/RegAllocVerifierTest.java | 1657 ++++++++--------- .../lir/alloc/verifier/AllocationState.java | 6 +- .../alloc/verifier/AllocationStateMap.java | 81 +- .../alloc/verifier/BlockVerifierState.java | 360 ++-- .../lir/alloc/verifier/CalleeSaveMap.java | 43 +- .../lir/alloc/verifier/ConflictResolver.java | 80 - .../verifier/ConflictedAllocationState.java | 57 +- ...nstantMaterializationConflictResolver.java | 226 --- .../verifier/FromUsageResolverGlobal.java | 103 +- .../lir/alloc/verifier/RAVConstant.java | 78 + .../lir/alloc/verifier/RAVInstruction.java | 101 +- .../{RARegister.java => RAVRegister.java} | 12 +- .../compiler/lir/alloc/verifier/RAValue.java | 65 +- .../lir/alloc/verifier/RAVariable.java | 2 +- .../lir/alloc/verifier/ReferencesBuilder.java | 10 +- .../lir/alloc/verifier/RegAllocVerifier.java | 57 +- .../alloc/verifier/RegAllocVerifierPhase.java | 461 +++-- .../verifier/UnknownAllocationState.java | 11 +- .../verifier/UnknownInstructionError.java | 4 +- .../alloc/verifier/ValueAllocationState.java | 71 +- .../alloc/verifier/VariableSynonymMap.java | 147 +- .../lir/alloc/verifier/VerifierPrinter.java | 18 +- .../AliveConstraintViolationException.java | 6 +- ...leeSavedRegisterNotRetrievedException.java | 12 +- ...onstantRematerializedToStackException.java | 18 +- .../InvalidRegisterUsedException.java | 5 +- .../JavaKindReferenceMismatchException.java} | 34 +- .../KindsMismatchException.java | 10 +- .../MissingLocationException.java | 4 +- .../MissingReferenceException.java | 14 +- .../OperandFlagMismatchException.java | 9 +- .../{ => exceptions}/RAVException.java | 7 +- .../RAVFailedVerificationException.java | 4 +- .../ValueNotInRegisterException.java | 16 +- .../lir/alloc/verifier/package-info.java | 254 +++ .../compiler/lir/dfa/UniqueWorkList.java | 19 +- 36 files changed, 2064 insertions(+), 1998 deletions(-) delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java delete mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVConstant.java rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{RARegister.java => RAVRegister.java} (87%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/AliveConstraintViolationException.java (91%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/CalleeSavedRegisterNotRetrievedException.java (78%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/ConstantRematerializedToStackException.java (73%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/InvalidRegisterUsedException.java (92%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{RematerializedConstantSourceMissingError.java => exceptions/JavaKindReferenceMismatchException.java} (53%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/KindsMismatchException.java (90%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/MissingLocationException.java (92%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/MissingReferenceException.java (80%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/OperandFlagMismatchException.java (89%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/RAVException.java (91%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/RAVFailedVerificationException.java (95%) rename compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/{ => exceptions}/ValueNotInRegisterException.java (84%) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/package-info.java diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index 035fb35665dc..e80f8a6f66c9 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -24,37 +24,43 @@ */ package jdk.graal.compiler.core.test; +import static jdk.graal.compiler.lir.LIRInstruction.OperandFlag.REG; +import static jdk.graal.compiler.lir.LIRInstruction.OperandFlag.STACK; + import jdk.graal.compiler.code.CompilationResult; import jdk.graal.compiler.core.common.CompilationIdentifier; import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; +import jdk.graal.compiler.core.phases.HighTier; +import jdk.graal.compiler.java.BytecodeParserOptions; import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRInstructionClass; import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.lir.ValueProcedure; import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.lir.alloc.RegisterAllocationPhase; -import jdk.graal.compiler.lir.alloc.verifier.AliveConstraintViolationException; -import jdk.graal.compiler.lir.alloc.verifier.CalleeSavedRegisterNotRetrievedException; +import jdk.graal.compiler.lir.alloc.verifier.UnknownAllocationState; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.AliveConstraintViolationException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.CalleeSavedRegisterNotRetrievedException; import jdk.graal.compiler.lir.alloc.verifier.ConflictedAllocationState; -import jdk.graal.compiler.lir.alloc.verifier.ConstantRematerializedToStackException; -import jdk.graal.compiler.lir.alloc.verifier.InvalidRegisterUsedException; -import jdk.graal.compiler.lir.alloc.verifier.KindsMismatchException; -import jdk.graal.compiler.lir.alloc.verifier.MissingLocationException; -import jdk.graal.compiler.lir.alloc.verifier.MissingReferenceException; -import jdk.graal.compiler.lir.alloc.verifier.OperandFlagMismatchException; -import jdk.graal.compiler.lir.alloc.verifier.RAVException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.ConstantRematerializedToStackException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.InvalidRegisterUsedException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.KindsMismatchException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.MissingLocationException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.MissingReferenceException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.OperandFlagMismatchException; +import jdk.graal.compiler.lir.alloc.verifier.RAVConstant; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVException; import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; import jdk.graal.compiler.lir.alloc.verifier.RAValue; import jdk.graal.compiler.lir.alloc.verifier.RAVariable; import jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase; import jdk.graal.compiler.lir.alloc.verifier.ValueAllocationState; -import jdk.graal.compiler.lir.alloc.verifier.ValueNotInRegisterException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.ValueNotInRegisterException; import jdk.graal.compiler.lir.asm.CompilationResultBuilder; import jdk.graal.compiler.lir.framemap.SimpleVirtualStackSlot; import jdk.graal.compiler.lir.gen.LIRGenerationResult; @@ -63,7 +69,6 @@ import jdk.graal.compiler.lir.phases.LIRSuites; import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.options.OptionValues; -import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.aarch64.AArch64; @@ -72,10 +77,8 @@ import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterAttributes; import jdk.vm.ci.code.RegisterConfig; -import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.code.ValueKindFactory; -import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; @@ -83,7 +86,6 @@ import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.PlatformKind; import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.Value; import jdk.vm.ci.meta.ValueKind; import org.junit.Assert; import org.junit.Before; @@ -95,9 +97,25 @@ import java.util.Map; import java.util.Set; -import static jdk.graal.compiler.lir.LIRInstruction.OperandFlag.REG; -import static jdk.graal.compiler.lir.LIRInstruction.OperandFlag.STACK; - +/** + * Test cases for {@link RegAllocVerifierPhase}, these work by injecting errors into incoming + * snippets and detecting that the exact exception is thrown. + * + *

+ * The test case first runs the {@link RegAllocVerifierPhase} on the same exact snippet, with the + * original {@link RegisterAllocationPhase register allocator} to make sure there are no issues with + * it. Then, the {@link RAVPhaseWrapper tainted verifier phase} is used, that injects a fault into + * verifier's instructions. {@link RegisterAllocationPhase Verifier phase} runs, and we expect it to + * throw an exception, this is then tested with assertions, and we look at its contents. + *

+ * + *

+ * Most of these tainted phases find the first candidate instruction and corrupt it. Some look for + * control-flow patterns to corrupt. Few change the {@link RegisterAllocationConfig register + * allocator config} to see if generated code adheres to it, while allocator runs with the correct + * config. Sometimes a scenario is easier to simulate directly with overwritten instructions. + *

+ */ public class RegAllocVerifierTest extends GraalCompilerTest { /** * Should the valid set of compiler phase suites be used? @@ -114,93 +132,270 @@ public class RegAllocVerifierTest extends GraalCompilerTest { */ RAVPhaseWrapper phase; - class RAVPhaseWrapper extends RegAllocVerifierPhase { + /** + * Disable inlining to force function calls during verification, off by default. + */ + boolean disableInlining = false; + + /** + * Base for tainted verifier phases, with helper functions. The + * {@link RAVPhaseWrapper#modifyVerifierInstructions} is where modifications are performed for + * detection. + */ + abstract class RAVPhaseWrapper extends RegAllocVerifierPhase { + @FunctionalInterface + interface InstructionScanFunction { + boolean apply(BasicBlock block, RAVInstruction.Base instruction); + } + + @FunctionalInterface + interface OpScanFunction { + boolean apply(BasicBlock block, RAVInstruction.Op op); + } + RAVPhaseWrapper() { super(null, null); } @Override - protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationPhase.AllocationContext context) { + protected final BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext context) { + var instructions = super.getVerifierInstructions(lir, preallocMap, context); + modifyVerifierInstructions(lir, instructions, context); + return instructions; + } + + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext context) { + // Overwritten when a modification is inserted into the resulting verifier instructions + // Does not always have to be used. + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { try { super.run(target, lirGenRes, context); } catch (RAVException e) { exception = e; } } - } - /** - * Overwrites a destination variable with a newly created one to cause a - * ValueNotInLocationException with an old variable being in the said place. - */ - class ChangeVariablePhase extends RAVPhaseWrapper { - protected RAVariable originalVariable; - protected RAVariable newVariable; + /** + * Last used variable id, incremented whenever {@link RAVPhaseWrapper#createNewVariable} is + * called. + */ + private int lastVariableId = -1; - @Override - protected Map saveInstructionsPreAlloc(LIR lir) { - var result = super.saveInstructionsPreAlloc(lir); - modifyLIR(lir, result); - return result; + protected RAVariable createNewVariable(LIR lir, ValueKind kind) { + if (lastVariableId == -1) { + lastVariableId = lir.numVariables(); + } + + var newVariable = new Variable(kind, ++lastVariableId); + return (RAVariable) RAValue.create(newVariable); } - protected void modifyLIR(LIR lir, Map instrMap) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - if (instruction instanceof StandardOp.LabelOp) { - continue; - } + protected RAVariable createNewVariable(LIR lir) { + return createNewVariable(lir, ValueKind.Illegal); + } + + protected RAVInstruction.Op createSymbolUsage(RAValue symbol, RAValue location) { + var usage = new RAVInstruction.Op(new StandardOp.NoOp(null, 0)); + + usage.uses = new RAVInstruction.ValueArrayPair(1); + usage.uses.curr[0] = location; + usage.uses.orig[0] = symbol; + usage.uses.operandFlags = new ArrayList<>(List.of(EnumSet.of(REG, STACK))); + + return usage; + } + + protected RAVInstruction.Base createSymbolSpawnOp(RAValue symbol, RAValue location) { + var lirInstruction = new StandardOp.NoOp(null, 0); + + if (symbol.isConstant()) { + return new RAVInstruction.ValueMove(lirInstruction, symbol.asConstant().getConstantValue(), location.getValue()); + } + + var op = new RAVInstruction.Op(lirInstruction); + + op.dests = new RAVInstruction.ValueArrayPair(1); + op.dests.curr[0] = location; + op.dests.orig[0] = symbol; + op.dests.operandFlags = new ArrayList<>(List.of(EnumSet.of(REG, STACK))); + + return op; + } + + /** + * Helper class for finding a fresh new location. + */ + static class UnusedValueTracker { + int highestVstackId; + Set allocatableRegs; + + UnusedValueTracker(RegisterAllocationConfig regAllocConfig) { + highestVstackId = -1; + allocatableRegs = new EconomicHashSet<>(regAllocConfig.getAllocatableRegisters()); + } + + void handleInstruction(RAVInstruction.Base instruction) { + switch (instruction) { + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + var curr = op.dests.curr[i]; + if (curr == null) { + continue; + } - var virInstr = instrMap.get(instruction); - if (virInstr instanceof RAVInstruction.Op op && changeVariable(lir, op)) { - break; + handleValue(curr); + } + } + case RAVInstruction.LocationMove move -> { + handleValue(move.to); + } + case RAVInstruction.ValueMove move -> { + handleValue(move.getLocation()); + } + default -> { } } } - } - protected boolean changeVariable(LIR lir, RAVInstruction.Op op) { - for (int i = 0; i < op.dests.count; i++) { - if (!op.dests.orig[i].isVariable()) { - continue; + void handleValue(RAValue value) { + if (value.isRegister()) { + allocatableRegs.remove(value.asRegister().getRegister()); + } else if (LIRValueUtil.isVirtualStackSlot(value.getValue())) { + var vstack = LIRValueUtil.asVirtualStackSlot(value.getValue()); + highestVstackId = Math.max(vstack.getId(), highestVstackId); } + } - var variable = op.dests.orig[i].asVariable(); - var newVariable = createNewVariable(lir, variable); + RAValue getResult(ValueKind kind) { + if (allocatableRegs.isEmpty()) { + return RAValue.create(new SimpleVirtualStackSlot(highestVstackId + 1, kind)); + } - op.dests.orig[i] = newVariable; + return RAValue.create(allocatableRegs.stream().iterator().next().asValue(kind)); + } + } - this.originalVariable = variable; - this.newVariable = newVariable; + /** + * Get unused value by other instructions. If a {@link Register} from + * {@link RegisterConfig#getAllocatableRegisters() allocatable registers} is not used, then + * it's prefered. Otherwise, a new {@link jdk.graal.compiler.lir.VirtualStackSlot}, with the + * highest id will be used. + * + * @param lir LIR + * @param instructions Verifier instructions + * @param context Allocation context + * @return New, unused value + */ + protected RAValue getUnusedValue(LIR lir, BlockMap> instructions, AllocationContext context) { + return getUnusedValue(lir, instructions, context, ValueKind.Illegal); + } - return true; + protected RAValue getUnusedValue(LIR lir, BlockMap> instructions, AllocationContext context, ValueKind kind) { + var tracker = new UnusedValueTracker(context.registerAllocationConfig); + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructionsForBlock = instructions.get(block); + for (var instruction : instructionsForBlock) { + tracker.handleInstruction(instruction); + } + } + + return tracker.getResult(kind); + } + + /** + * Go over instructions, run the scanFuntion, if true a modification occurred and stop. + * + * @param lir LIR + * @param instructions Verifier instructions + * @param scanFunction Modification function + * @return true, if modification occurred, otherwise false + */ + protected boolean scanOverInstructions(LIR lir, BlockMap> instructions, InstructionScanFunction scanFunction) { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + var instructionsForBlock = instructions.get(block); + for (var instruction : instructionsForBlock) { + if (scanFunction.apply(block, instruction)) { + return true; + } + } } return false; } - protected RAVariable createNewVariable(LIR lir, RAVariable oldVariable) { - var newVariable = new Variable(oldVariable.getValue().getValueKind(), lir.numVariables() + 1); - return (RAVariable) RAValue.create(newVariable); + /** + * Same as {@link RAVPhaseWrapper#scanOverInstructions}, but only for + * {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.Op}. + * + * @param lir LIR + * @param instructions Verifier instructions + * @param scanFunction Modification function + * @return true, if modification occurred, otherwise false + */ + protected boolean scanOps(LIR lir, BlockMap> instructions, OpScanFunction scanFunction) { + return scanOverInstructions(lir, instructions, (block, instruction) -> { + if (instruction instanceof RAVInstruction.Op op) { + return scanFunction.apply(block, op); + } + + return false; + }); + } + } + + /** + * Overwrite first seen variable with a fresh new one, to detect + * {@link ValueNotInRegisterException} with @{link {@link ValueAllocationState}}. + */ + class ChangeVariablePhase extends RAVPhaseWrapper { + protected RAVariable originalVariable; + protected RAVariable newVariable; + + @Override + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext ctx) { + scanOps(lir, instructions, (block, op) -> { + for (int i = 0; i < op.dests.count; i++) { + var curr = op.dests.curr[i]; + var orig = op.dests.orig[i]; + if (curr.equals(orig) || !orig.isVariable()) { + continue; + } + + var variable = op.dests.orig[i].asVariable(); + var newVariable = createNewVariable(lir, variable.getLIRKind()); + + op.dests.orig[i] = newVariable; + + this.originalVariable = variable; + this.newVariable = newVariable; + + return true; + } + + return false; + }); } } /** - * This pass changes register allocation config to only allow certain registers to be used for - * allocation, and we want the verifier to detect usage of the said register. + * Change the register allocator config the verifier sees with one register being restricted + * from allocation. This register is chosen simply by finding the first register the allocator + * used. */ class DisallowedRegisterPhase extends RAVPhaseWrapper { protected Register ignoredReg; protected RegisterAllocationConfig regAllocConfig; @Override - protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext context) { - var restrictedTo = getAllocationRestrictedToArray(lir, context.registerAllocationConfig); + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext context) { + setFirstAllocatedRegister(lir, instructions); + var restrictedTo = getAllocationRestrictedToArray(context.registerAllocationConfig); regAllocConfig = new RegisterAllocationConfig(context.registerAllocationConfig.getRegisterConfig(), restrictedTo); - - return super.getVerifierInstructions(lir, preallocMap, context); } @Override @@ -208,232 +403,129 @@ protected RegisterAllocationConfig getRegisterAllocationConfig(AllocationContext return regAllocConfig; } - protected void modifyAllocatableRegisterList(LIR lir, List registerList) { - registerList.remove(findAllocatedRegister(lir)); - } - - protected String[] getAllocationRestrictedToArray(LIR lir, RegisterAllocationConfig cfg) { - var regAllocatedTo = findAllocatedRegister(lir); + /** + * Create allocationRestrictedTo array for {@link RegisterAllocationConfig} without the + * selected register. + * + * @param cfg Original config + * @return New allocationRestrictedTo array without ignored register + */ + protected String[] getAllocationRestrictedToArray(RegisterAllocationConfig cfg) { var allocatableRegs = cfg.getAllocatableRegisters(); String[] restrictedTo = new String[allocatableRegs.size() - 1]; + int i = 0; for (var reg : allocatableRegs) { - if (i >= allocatableRegs.size() - 1) { - continue; - } - - if (reg.equals(regAllocatedTo)) { + if (reg.equals(ignoredReg)) { continue; } restrictedTo[i++] = reg.toString(); + if (i >= allocatableRegs.size() - 1) { + break; + } } - ignoredReg = regAllocatedTo; return restrictedTo; } - protected Register findAllocatedRegister(LIR lir) { - final Register[] allocatedTo = {null}; - for (var blockId : lir.getBlocks()) { - if (allocatedTo[0] != null) { - break; - } - - var block = lir.getBlockById(blockId); - var instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - if (instruction instanceof StandardOp.LabelOp) { - continue; - } + /** + * Find the first register the allocator used, set it and stop. + * + * @param lir LIR + * @param instructions Verifier instructions + */ + protected void setFirstAllocatedRegister(LIR lir, BlockMap> instructions) { + scanOverInstructions(lir, instructions, (block, instruction) -> { + switch (instruction) { + case RAVInstruction.Op op -> { + for (int i = 0; i < op.dests.count; i++) { + var curr = op.dests.curr[i]; + var orig = op.dests.orig[i]; + if (curr.equals(orig)) { + continue; + } - instruction.forEachOutput(new ValueProcedure() { - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (value instanceof RegisterValue regValue) { - allocatedTo[0] = regValue.getRegister(); + if (curr.isRegister()) { + ignoredReg = curr.asRegister().getRegister(); + return true; } - return value; } - }); - - if (allocatedTo[0] != null) { - break; + } + case RAVInstruction.LocationMove move -> { + if (move.to.isRegister()) { + ignoredReg = move.to.asRegister().getRegister(); + return true; + } + } + default -> { } } - } - return allocatedTo[0]; + + return false; + }); } } /** - * Change a register only used once as output, so that state of it is - * {@link jdk.graal.compiler.lir.alloc.verifier.UnknownAllocationState unknown}. + * Change output of a first instruction to a different register, so that when it's output is + * used somewhere, the state is {@link UnknownAllocationState}. This output needs to generate a + * symbol if true, no symbol is generated. */ class ForceUnknownStateInRegister extends RAVPhaseWrapper { - protected RAVariable variable; - protected RegisterValue oldRegister = null; - protected RegisterValue newRegister; - protected int idx = 0; - - protected Map regUsage; - - ForceUnknownStateInRegister() { - super(); - this.regUsage = new EconomicHashMap<>(); - } + protected RAValue symbol; + protected RAValue oldLocation; + protected RAValue newLocation; @Override - protected BlockMap> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { - modifyLIR(lir, instrMap, context); - return super.getVerifierInstructions(lir, instrMap, context); - } - - protected void modifyLIR(LIR lir, Map instrMap, AllocationContext context) { - var replacementReg = getUnusedAllowedRegister(lir, context.registerAllocationConfig); - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - if (!(instruction instanceof StandardOp.LabelOp) && instrMap.containsKey(instruction)) { - var op = (RAVInstruction.Op) instrMap.get(instruction); - - if (handleOp(op, replacementReg)) { - return; - } - - if (handleVirtualMoves(op, replacementReg)) { - return; - } - } - - handleRegisterUsage(instruction); - } - } - } - - protected boolean handleOp(RAVInstruction.Op op, Register replacementReg) { - idx = 0; - op.getLIRInstruction().forEachOutput(new ValueProcedure() { - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (ValueUtil.isRegister(value) && op.dests.orig[idx].isVariable() && oldRegister == null && !regUsage.containsKey(ValueUtil.asRegister(value))) { - oldRegister = ValueUtil.asRegisterValue(value); - newRegister = replacementReg.asValue(oldRegister.getValueKind()); - variable = op.dests.orig[idx].asVariable(); - return newRegister; + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext context) { + var regValue = getUnusedValue(lir, instructions, context); + + scanOps(lir, instructions, (block, op) -> { + for (int i = 0; i < op.dests.count; i++) { + var curr = op.dests.curr[i]; + var orig = op.dests.orig[i]; + if (curr.equals(orig)) { + continue; } - idx++; - return value; - } - }); - - return newRegister != null; - } + symbol = orig; + oldLocation = curr; + newLocation = regValue; + op.dests.curr[i] = regValue; - protected boolean handleVirtualMoves(RAVInstruction.Op op, Register replacementReg) { - for (var move : op.getVirtualMoveList()) { - var locValue = move.getLocation().getValue(); - if (ValueUtil.isRegister(locValue)) { - var register = ValueUtil.asRegister(locValue); - - if (move.variableOrConstant.isVariable() && !regUsage.containsKey(register)) { - oldRegister = ValueUtil.asRegisterValue(locValue); - newRegister = replacementReg.asValue(locValue.getValueKind()); - variable = move.variableOrConstant.asVariable(); - move.setLocation(RAValue.create(newRegister)); - return true; - } - - regUsage.put(register, regUsage.getOrDefault(register, 1) + 1); + return true; } - } - return false; - } - protected void handleRegisterUsage(LIRInstruction instruction) { - instruction.forEachOutput(new ValueProcedure() { - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (ValueUtil.isRegister(value)) { - var register = ValueUtil.asRegister(value); - regUsage.put(register, regUsage.getOrDefault(register, 1) + 1); - } - - return value; - } + return false; }); } - - protected Register getUnusedAllowedRegister(LIR lir, RegisterAllocationConfig cfg) { - Map usedRegisters = new EconomicHashMap<>(); - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - instruction.forEachOutput(new ValueProcedure() { - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (ValueUtil.isRegister(value)) { - var register = ValueUtil.asRegister(value); - usedRegisters.put(register, usedRegisters.getOrDefault(register, 1) + 1); - } - - return value; - } - }); - } - } - - for (var reg : cfg.getAllocatableRegisters()) { - if (usedRegisters.containsKey(reg)) { - continue; - } - - return reg; - } - - return null; - } - } /** - * Change the kind of operand to trigger a KindsMismatchException, very simply, find the first - * instruction that is not a label and look through its operand array to find the first variable - * and change its type to Illegal. + * Change the kind of operand to trigger a {@link KindsMismatchException}, very simply, find the + * first instruction and look through its operand array to find the first variable and change + * its type to Illegal. */ abstract class ChangeKindPhase extends RAVPhaseWrapper { protected Variable variable; @Override - protected Map saveInstructionsPreAlloc(LIR lir) { - var instrMap = super.saveInstructionsPreAlloc(lir); - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - if (instruction instanceof StandardOp.LabelOp) { - continue; - } - - var op = (RAVInstruction.Op) instrMap.get(instruction); - if (op == null) { - continue; - } - - if (changeVariableKind(getTargetValueArrayPair(op))) { - return instrMap; - } - } - } - - return instrMap; + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext ctx) { + scanOps(lir, instructions, (block, op) -> { + var values = getTargetValueArrayPair(op); + return changeVariableKind(values); + }); } protected abstract RAVInstruction.ValueArrayPair getTargetValueArrayPair(RAVInstruction.Op op); + /** + * Change the first-found variable and change its kind to {@link ValueKind#Illegal}. + * + * @param values Values we are changing kind of + * @return true, if change occured, otherwise false. + */ protected boolean changeVariableKind(RAVInstruction.ValueArrayPair values) { for (int i = 0; i < values.count; i++) { var orig = values.orig[i]; @@ -478,82 +570,60 @@ protected RAVInstruction.ValueArrayPair getTargetValueArrayPair(RAVInstruction.O } /** - * Modifies LIR instruction location in a way where an alive operand and destination or - * temporary use the same register. - * - *

- * Finds the first instruction that satisfies having both alive operand and temp/output and - * changes it so one location is the same. - *

+ * First the first instruction that has alive inputs, as well as an output. Change the output + * and alive operand to use the same location so that the input operand does not survive. */ abstract class ViolateAliveConstraint extends RAVPhaseWrapper { - class SetAliveRegProc implements ValueProcedure { - boolean first = true; - Value aliveValue; - - SetAliveRegProc(Value aliveValue) { - this.aliveValue = aliveValue; - } - - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - if (first) { - var registerValue = (RegisterValue) aliveValue; - var register = registerValue.getRegister(); - - first = false; + RAValue location; - return register.asValue(value.getValueKind()); + @Override + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext context) { + boolean found = scanOps(lir, instructions, (block, op) -> { + if (op.alive.count == 0) { + return false; } - return value; - } - } + var values = getValues(op); + if (values.count == 0) { + return false; + } - @Override - protected BlockMap> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - if (instruction instanceof StandardOp.LabelOp) { - continue; - } + if (values.curr[0].isIllegal()) { + return false; + } - if (!instrMap.containsKey(instruction)) { - continue; - } + location = op.alive.curr[0]; + values.curr[0] = location; - var op = (RAVInstruction.Op) instrMap.get(instruction); - if (op.alive.count == 0) { - continue; - } + return true; + }); - var aliveValue = findAllocatedAliveValue(instruction); - var setAliveRegProc = new SetAliveRegProc(aliveValue); - changeLocationInTarget(instruction, setAliveRegProc); - if (!setAliveRegProc.first) { - return super.getVerifierInstructions(lir, instrMap, context); - } - } + if (found) { + return; } - return super.getVerifierInstructions(lir, instrMap, context); + /* + * Create an operation that has alive and output with same location to see that it can + * be detected, if it was not found in the input snippet. + */ + var lirInstruction = new StandardOp.NoOp(null, 0); + var op = new RAVInstruction.Op(lirInstruction); + op.temp = new RAVInstruction.ValueArrayPair(1); + op.alive = new RAVInstruction.ValueArrayPair(1); + + location = getUnusedValue(lir, instructions, context); + op.temp.curr[0] = location; + op.temp.orig[0] = location; + op.temp.operandFlags = new ArrayList<>(List.of(EnumSet.of(REG, STACK))); + op.alive.curr[0] = location; + op.alive.orig[0] = location; + op.alive.operandFlags = new ArrayList<>(List.of(EnumSet.of(REG, STACK))); + + var b0Instructions = instructions.get(0); + b0Instructions.add(b0Instructions.size() - 1, op); } - protected abstract void changeLocationInTarget(LIRInstruction instruction, ValueProcedure setAliveRegProc); - - protected Value findAllocatedAliveValue(LIRInstruction instruction) { - final Value[] alive = {null}; - instruction.forEachAlive(new ValueProcedure() { - @Override - public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet flags) { - alive[0] = value; - return value; - } - }); - return alive[0]; - } + protected abstract RAVInstruction.ValueArrayPair getValues(RAVInstruction.Op op); } /** @@ -561,184 +631,100 @@ public Value doValue(Value value, LIRInstruction.OperandMode mode, EnumSet conflictVariables; @Override - protected Map saveInstructionsPreAlloc(LIR lir) { - var instrMap = super.saveInstructionsPreAlloc(lir); - - var startBlock = lir.getControlFlowGraph().getStartBlock(); - - BasicBlock conflictBlock = getConflictUseBlock(lir); - assert conflictBlock != null; - - var variables = getVariablesFromVirtualMoves(lir, startBlock, instrMap); - - RAVariable targetVariable = getUsedVariableFromBlock(lir, conflictBlock, instrMap, variables); - assert targetVariable != null; - - var branchBlock = getConflictSourceBlock(lir, conflictBlock); - addVirtualMove(lir, branchBlock, instrMap, targetVariable, variables); - - return instrMap; - } - - /** - * Get a block where a location with a conflicted state will be used. - * - * @param lir LIR - * @return Conflicted use block - */ - protected abstract BasicBlock getConflictUseBlock(LIR lir); - - /** - * Get block where conflict will be created, by inserting a ValueMove instruction. - * - * @param lir LIR - * @param conflictBlock Block where conflict will be used - * @return Source of the conflict - */ - protected abstract BasicBlock getConflictSourceBlock(LIR lir, BasicBlock conflictBlock); - - protected Map getVariablesFromVirtualMoves(LIR lir, BasicBlock block, Map instrMap) { - var variables = new EconomicHashMap(); - var startInstructions = lir.getLIRforBlock(block); - for (var instruction : startInstructions) { - var op = (RAVInstruction.Op) instrMap.get(instruction); - if (op == null) { - continue; - } - - for (var move : op.getVirtualMoveList()) { - if (move.variableOrConstant.isVariable()) { - variables.put(move.variableOrConstant.asVariable(), move.getLocation()); - } - } + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext ctx) { + var conflictLocation = getUnusedValue(lir, instructions, ctx); + var conflictBlock = getFirstConflictBlock(lir); + + conflictVariables = new EconomicHashSet<>(); + for (int i = 0; i < conflictBlock.getPredecessorCount(); i++) { + var pred = conflictBlock.getPredecessorAt(i); + var instructionsForPred = instructions.get(pred); + + var idx = instructionsForPred.size() - 2; + var variable = createNewVariable(lir); + var op = createSymbolSpawnOp(variable, conflictLocation); + + // New symbol inserted into same conflicting location. + conflictVariables.add(new ValueAllocationState(variable, null, null)); + instructionsForPred.add(idx, op); } - return variables; - } - protected RAVariable getUsedVariableFromBlock(LIR lir, BasicBlock block, Map instrMap, Map variables) { - var endInstructions = lir.getLIRforBlock(block); - for (var instruction : endInstructions) { - var op = (RAVInstruction.Op) instrMap.get(instruction); - if (op == null) { - continue; - } - - for (int i = 0; i < op.uses.count; i++) { - var orig = op.uses.orig[i]; - if (!orig.isVariable()) { - continue; - } + targetVariable = createNewVariable(lir); + // Here the target varible will not be read, instead it will be conflicted + var usage = createSymbolUsage(targetVariable, conflictLocation); + var usageIdx = 1; + instructions.get(conflictBlock).add(usageIdx, usage); + } - if (variables.containsKey(orig.asVariable())) { - return orig.asVariable(); - } + protected BasicBlock getFirstConflictBlock(LIR lir) { + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + if (block.getPredecessorCount() > 1 && isConflictBlock(block)) { + return block; } } return null; } - /** - * Adds a conflict-inducing value move to a block. - * - * @param lir LIR - * @param block Block where we are putting move to - * @param instrMap Pre-allocation instruction map - * @param targetVariable Target variable we are creating conflict with - * @param variables Variables and their locations from previous steps - */ - protected void addVirtualMove(LIR lir, BasicBlock block, Map instrMap, RAVariable targetVariable, Map variables) { - var instructions = lir.getLIRforBlock(block); - - boolean first = true; - for (var instruction : instructions.reversed()) { - if (first) { - first = false; - continue; - } - - var op = (RAVInstruction.Op) instrMap.get(instruction); - if (op == null) { - continue; - } - - var newVar = new Variable(targetVariable.getVariable().getValueKind(), lir.numVariables() + 1); - op.addVirtualMove(new RAVInstruction.ValueMove(null, newVar, variables.get(targetVariable).getValue())); - - this.newVariable = (RAVariable) RAValue.create(newVar); - this.targetVariable = targetVariable; - - break; - } - } + protected abstract boolean isConflictBlock(BasicBlock block); } + /** + * Cause a conflict in an if-statement merge block. + */ class DiamondConflictPhase extends ConflictPhase { @Override - protected BasicBlock getConflictUseBlock(LIR lir) { - BasicBlock endBlock = null; - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - if (block.getSuccessorCount() == 0) { - endBlock = block; - break; - } - } - return endBlock; - } - - @Override - protected BasicBlock getConflictSourceBlock(LIR lir, BasicBlock conflictBlock) { - return lir.getControlFlowGraph().getStartBlock().getSuccessorAt(0); + protected boolean isConflictBlock(BasicBlock block) { + return !block.isLoopHeader(); } } + /** + * Cause a conflict in a loop header. + */ class LoopConflictPhase extends ConflictPhase { @Override - protected BasicBlock getConflictUseBlock(LIR lir) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - - if (block.isLoopHeader()) { - return block; - } - } - return null; - } - - @Override - protected BasicBlock getConflictSourceBlock(LIR lir, BasicBlock loopBlock) { - for (int i = 0; i < loopBlock.getSuccessorCount(); i++) { - var succ = loopBlock.getSuccessorAt(i); - for (int j = 0; j < succ.getSuccessorCount(); j++) { - if (succ.getSuccessorAt(j).equals(loopBlock)) { - return succ; - } - } - } - return null; + protected boolean isConflictBlock(BasicBlock block) { + return block.isLoopHeader(); } } + /** + * Trigger {@link CalleeSavedRegisterNotRetrievedException}, by creating a new + * {@link RegisterAllocationConfig} with a new callee-saved register. This register will be + * overwritten by the body of the method and will not be retrieved. + * + *

+ * The selected register is dependent on the architecture, using the first argument register, + * that will be used. + *

+ */ class CalleeSavePhase extends RAVPhaseWrapper { Register register; @@ -746,6 +732,7 @@ class CalleeSavePhase extends RAVPhaseWrapper { protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { var name = target.arch.getName(); + // Select first paramter register for this switch (name) { case "AMD64" -> register = AMD64.rsi; case "ARM64" -> register = AArch64.r0; @@ -814,12 +801,17 @@ public boolean areAllAllocatableRegistersCallerSaved() { } } + /** + * Trigger a rematerialization of a constant to a stack slot, where its forbidden via + * {@link LoadConstOp#canRematerializeToStack()}. Triggered by inserting the scenario of a + * constant that is used in this way. + */ class PhantomConstRematerializationPhase extends RAVPhaseWrapper { RAVariable variableValue; RAValue stackSlotValue; ConstantValue constant; - class LoadConstOp extends LIRInstruction implements StandardOp.LoadConstantOp { + static class LoadConstOp extends LIRInstruction implements StandardOp.LoadConstantOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(LoadConstOp.class); @Def({REG, STACK}) protected AllocatableValue result; @@ -853,11 +845,26 @@ public void emitCode(CompilationResultBuilder crb) { } @Override - protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { - var instructions = super.getVerifierInstructions(lir, preallocMap, ctx); - + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext ctx) { var kind = LIRKind.value(AMD64Kind.V32_BYTE); - var jvConst = new JavaConstant() { + + constant = new ConstantValue(kind, getConstant()); + var stackSlot = new SimpleVirtualStackSlot(1, kind); + + stackSlotValue = RAValue.create(stackSlot); + var constantValue = new RAVConstant(constant, false); + + var remMove = new RAVInstruction.ValueMove(new LoadConstOp(stackSlot, constant.getJavaConstant()), constant, stackSlot); + + var usage = createSymbolUsage(constantValue, stackSlotValue); + + var blockInstructions = instructions.get(0); + blockInstructions.add(blockInstructions.size() - 1, remMove); + blockInstructions.add(blockInstructions.size() - 1, usage); + } + + protected JavaConstant getConstant() { + return new JavaConstant() { @Override public JavaKind getJavaKind() { return JavaKind.Int; @@ -903,210 +910,142 @@ public double asDouble() { return 0; } }; - - constant = new ConstantValue(kind, jvConst); - var stackSlot = new SimpleVirtualStackSlot(1, kind); - var variable = new Variable(kind, lir.numVariables() + 1); - - stackSlotValue = RAValue.create(stackSlot); - variableValue = (RAVariable) RAValue.create(variable); - - var loadConstOp = new LoadConstOp(variable, constant.getJavaConstant()); - var constSpawnOp = new RAVInstruction.Op(loadConstOp); - loadConstOp.forEachOutput(constSpawnOp.dests.copyOriginalProc); - constSpawnOp.dests.curr[0] = stackSlotValue; - - var remMove = new RAVInstruction.ValueMove(new LoadConstOp(stackSlot, constant.getJavaConstant()), constant, stackSlot); - - var usage = new RAVInstruction.Op(new StandardOp.NoOp(null, 0)); - usage.uses = new RAVInstruction.ValueArrayPair(1); - usage.uses.curr[0] = stackSlotValue; - usage.uses.orig[0] = variableValue; - - var blockInstructions = instructions.get(0); - blockInstructions.add(1, constSpawnOp); - blockInstructions.add(blockInstructions.size() - 1, remMove); - blockInstructions.add(blockInstructions.size() - 1, usage); - - return instructions; } } + /** + * Detect a violation of {@link LIRInstruction.OperandFlag} being missing/different. Done by + * finding the first instruction that has an input that is changed by the allocator. + */ class ForceOperandFlagPhase extends RAVPhaseWrapper { @Override - protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { - var instructions = super.getVerifierInstructions(lir, preallocMap, ctx); - - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructionsForBlock = instructions.get(block); - for (var instruction : instructionsForBlock) { - if (instruction instanceof RAVInstruction.Op op) { - if (op.isLabel()) { - continue; - } - - for (int i = 0; i < op.uses.count; i++) { - var curr = op.uses.curr[i]; - var orig = op.uses.orig[i]; + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext ctx) { + scanOps(lir, instructions, (block, op) -> { + if (op.isLabel()) { + return false; + } - if (orig.equals(curr)) { - continue; - } + for (int i = 0; i < op.uses.count; i++) { + var curr = op.uses.curr[i]; + var orig = op.uses.orig[i]; - EnumSet opFlags = EnumSet.noneOf(LIRInstruction.OperandFlag.class); - opFlags.addAll(op.uses.operandFlags.get(i)); + if (orig.equals(curr)) { + continue; + } - if (curr.isRegister()) { - opFlags.remove(LIRInstruction.OperandFlag.REG); - } else if (LIRValueUtil.isStackSlotValue(curr.getValue())) { - opFlags.remove(LIRInstruction.OperandFlag.STACK); - } else if (LIRValueUtil.isConstantValue(curr.getValue())) { - opFlags.remove(LIRInstruction.OperandFlag.CONST); - } else { - continue; - } + EnumSet opFlags = EnumSet.noneOf(LIRInstruction.OperandFlag.class); + opFlags.addAll(op.uses.operandFlags.get(i)); - op.uses.operandFlags.set(i, opFlags); - return instructions; - } + if (curr.isRegister()) { + opFlags.remove(REG); + } else if (LIRValueUtil.isStackSlotValue(curr.getValue())) { + opFlags.remove(STACK); + } else if (LIRValueUtil.isConstantValue(curr.getValue())) { + opFlags.remove(LIRInstruction.OperandFlag.CONST); + } else { + continue; } + + op.uses.operandFlags.set(i, opFlags); + return true; } - } - return instructions; + return false; + }); } } + /** + * Force a missing location (the case where the allocation location is null). Again, the first + * instruction touched by the allocator is chosen. + */ class ForceMissingLocationExceptionPhase extends RAVPhaseWrapper { @Override - protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { - var instructions = super.getVerifierInstructions(lir, preallocMap, ctx); - - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructionsForBlock = instructions.get(block); - for (var instruction : instructionsForBlock) { - if (instruction instanceof RAVInstruction.Op op) { - if (op.isLabel()) { - continue; - } - - for (int i = 0; i < op.uses.count; i++) { - var curr = op.uses.curr[i]; - var orig = op.uses.orig[i]; + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext ctx) { + scanOps(lir, instructions, (block, op) -> { + if (op.isLabel()) { + return false; + } - if (orig.equals(curr)) { - continue; - } + for (int i = 0; i < op.uses.count; i++) { + var curr = op.uses.curr[i]; + var orig = op.uses.orig[i]; - op.uses.curr[i] = null; - return instructions; - } + if (orig.equals(curr)) { + continue; } + + op.uses.curr[i] = null; + return true; } - } - return instructions; + return false; + }); } } + /** + * Add a new reference to a list of references. This reference will be at a location that a does + * not have a reference, this triggers {@link MissingReferenceException}. + */ class ExcessReferencePhase extends RAVPhaseWrapper { - @Override - protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { - var instructions = super.getVerifierInstructions(lir, preallocMap, ctx); + BasicBlock currentBlock; + RAVInstruction.Op prev; - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructionsForBlock = instructions.get(block); + @Override + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext ctx) { + currentBlock = null; + prev = null; + + scanOps(lir, instructions, (block, op) -> { + if (block != currentBlock) { + currentBlock = block; + prev = null; + } - RAVInstruction.Op prev = null; - for (var instruction : instructionsForBlock) { - if (instruction instanceof RAVInstruction.Op op) { - if (prev != null) { - var curr = prev.dests.curr[0]; - var kind = curr.getLIRKind().makeUnknownReference(); - var refLocation = RAValue.create(curr.asRegister().getRegister().asValue(kind)); - - op.references = new EconomicHashSet<>(); - op.references.add(refLocation); - return instructions; - } + if (prev != null) { + var curr = prev.dests.curr[0]; + var kind = curr.getLIRKind().makeUnknownReference(); + var refLocation = RAValue.create(curr.asRegister().getRegister().asValue(kind)); - if (op.isLabel()) { - continue; - } + op.references = new EconomicHashSet<>(); + op.references.add(refLocation); + return true; + } - if (op.dests.count == 0) { - continue; - } + if (op.isLabel()) { + return false; + } - var curr = op.dests.curr[0]; - if (!curr.isRegister() || !curr.getLIRKind().isValue()) { - continue; - } + if (op.dests.count == 0) { + return false; + } - // This instruction will define dest location - // next instruction will verify the reference list - // -> no reference there! - prev = op; - } + var curr = op.dests.curr[0]; + if (!curr.isRegister() || !curr.getLIRKind().isValue()) { + return false; } - } - return instructions; + // This instruction will define dest location + // next instruction will verify the reference list + // -> no reference there! + prev = op; + return false; + }); } } + /** + * Test if a synthetically inserted variable gets its location chosen correctly by + * {@link jdk.graal.compiler.lir.alloc.verifier.FromUsageResolverGlobal}. + */ class PhiResolver extends RAVPhaseWrapper { RAVInstruction.Op label; RAValue location; @Override - protected BlockMap> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { - var instructions = super.getVerifierInstructions(lir, instrMap, context); - - Set usedRegisters = new EconomicHashSet<>(); - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructionsForBlock = instructions.get(block); - for (var instruction : instructionsForBlock) { - switch (instruction) { - case RAVInstruction.Op op -> { - for (int i = 0; i < op.dests.count; i++) { - var dest = op.dests.curr[i]; - if (dest.isRegister()) { - usedRegisters.add(dest.asRegister().getRegister()); - } - } - } - case RAVInstruction.ValueMove move -> { - var regValue = move.getLocation(); - if (regValue.isRegister()) { - usedRegisters.add(regValue.asRegister().getRegister()); - } - } - case RAVInstruction.LocationMove move -> { - if (move.to.isRegister()) { - usedRegisters.add(move.to.asRegister().getRegister()); - } - } - default -> { - } - } - } - } - - Register targetRegister = null; - var allocatableRegisters = context.registerAllocationConfig.getAllocatableRegisters(); - for (var register : allocatableRegisters) { - if (!usedRegisters.contains(register)) { - targetRegister = register; - break; - } - } - - assert targetRegister != null : "No register available for phi resolver test"; + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext context) { + RAValue target = getUnusedValue(lir, instructions, context); var labelOp = (RAVInstruction.Op) instructions.get(0).getFirst(); @@ -1117,60 +1056,58 @@ protected BlockMap> getVerifierInstructions(LIR lir, M newDst.operandFlags.add(labelOp.dests.operandFlags.get(i)); } - var variable = RAVariable.create(new Variable(ValueKind.Illegal, lir.numVariables() + 1)); - location = RAValue.create(targetRegister.asValue()); + var variable = createNewVariable(lir); + location = target; newDst.orig[labelOp.dests.count] = variable; newDst.curr[labelOp.dests.count] = null; - newDst.operandFlags.add(EnumSet.of(LIRInstruction.OperandFlag.REG)); + newDst.operandFlags.add(EnumSet.of(REG)); labelOp.dests = newDst; label = labelOp; - var usage = new RAVInstruction.Op(new StandardOp.NoOp(null, 0)); - usage.uses = new RAVInstruction.ValueArrayPair(1); - usage.uses.orig[0] = variable; - usage.uses.curr[0] = location; - usage.uses.operandFlags.add(EnumSet.of(LIRInstruction.OperandFlag.REG)); - + var usage = createSymbolUsage(variable, location); instructions.get(0).add(instructions.get(0).size() - 1, usage); - - return instructions; } } + /** + * Test if a reference gets deleted, if it's not tracked in the reference list. When + * {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.Op#references} is set (even + * empty), then only these references can survive, so an old one will get deleted. Triggers + * {@link ValueNotInRegisterException}, because it was purged. + */ class DeleteReferencePhase extends RAVPhaseWrapper { RAValue location; RAValue variable; + BasicBlock currentBlock; + RAVInstruction.Op prev; - protected BlockMap> getVerifierInstructions(LIR lir, Map instrMap, AllocationContext context) { - var instructions = super.getVerifierInstructions(lir, instrMap, context); - - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructionsForBlock = instructions.get(block); + @Override + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext context) { + currentBlock = null; + prev = null; + + scanOps(lir, instructions, (block, op) -> { + if (block != currentBlock) { + currentBlock = block; + prev = null; + } - RAVInstruction.Op prev = null; - for (var instruction : instructionsForBlock) { - if (instruction instanceof RAVInstruction.Op op) { - if (prev != null) { - if (setReferences(op.uses) || setReferences(op.alive)) { - prev.references = new EconomicHashSet<>(); - return instructions; - } - } + if (prev != null && (setReferences(op.uses) || setReferences(op.alive))) { + prev.references = new EconomicHashSet<>(); + return true; + } - if (!op.isLabel()) { - // Check fix, the previous instruction cannot define - // the reference being discarded, because then this phase - // won't work - prev = op; - } - } + if (!op.isLabel()) { + // Check fix, the previous instruction cannot define + // the reference being discarded, because then this phase + // won't work + prev = op; } - } - return instructions; + return false; + }); } protected boolean setReferences(RAVInstruction.ValueArrayPair values) { @@ -1192,10 +1129,133 @@ protected boolean setReferences(RAVInstruction.ValueArrayPair values) { } } + class ForgottenReloadPhase extends RAVPhaseWrapper { + RAValue spillSlot; + RAValue expectedReloadLocation; + RAValue redirectedReloadLocation; + + @Override + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext context) { + scanOverInstructions(lir, instructions, (block, instruction) -> { + if (!(instruction instanceof RAVInstruction.Reload reload)) { + return false; + } + + if (!isLocationUsedLater(instructions, block, instruction, reload.to)) { + return false; + } + + spillSlot = reload.from; + expectedReloadLocation = reload.to; + redirectedReloadLocation = getUnusedValue(lir, instructions, context, reload.to.getLIRKind()); + + reload.to = redirectedReloadLocation; + + // LocationMove is generally used instead of specialized Reload, + // so the destination needs to be overwritten in both cases. + // This should probably be changed. + ((RAVInstruction.LocationMove) reload).to = redirectedReloadLocation; + return true; + }); + } + + /** + * Check if the location is used later. + * + * @param instructions Verifier instructions + * @param currentBlock Block where it is supposed to be used + * @param currentInstruction Instruction from which we are looking for usage + * @param location The location in question + * @return true, if it was used in this block, otherwise false + */ + protected boolean isLocationUsedLater(BlockMap> instructions, BasicBlock currentBlock, RAVInstruction.Base currentInstruction, RAValue location) { + boolean foundCurrentInstruction = false; + var instructionsForBlock = instructions.get(currentBlock); + for (var instruction : instructionsForBlock) { + if (!foundCurrentInstruction) { + if (instruction == currentInstruction) { + foundCurrentInstruction = true; + } + continue; + } + + if (usesLocation(instruction, location)) { + return true; + } + } + + return false; + } + + protected boolean usesLocation(RAVInstruction.Base instruction, RAValue location) { + if (instruction instanceof RAVInstruction.Op op) { + return isLocationInValues(op.uses, location) || isLocationInValues(op.alive, location); + } + return false; + } + + protected boolean isLocationInValues(RAVInstruction.ValueArrayPair values, RAValue location) { + for (int i = 0; i < values.count; i++) { + if (location.equals(values.curr[i])) { + return true; + } + } + + return false; + } + } + + /** + * Complete overwrite the incoming snippet code with early reuse violation. + * + *

+ * Simulate a variable being created, immediately overwritten, and then used. This throws a + * {@link ValueNotInRegisterException}, because the variable is not present, and instead a + * different value is present. + *

+ */ + class EarlyReusePhase extends RAVPhaseWrapper { + RAVariable liveVariable; + RAVariable overwrittenVariable; + RAValue location; + + @Override + protected void modifyVerifierInstructions(LIR lir, BlockMap> instructions, AllocationContext context) { + location = getUnusedValue(lir, instructions, context, LIRKind.Illegal); + + liveVariable = createNewVariable(lir, location.getLIRKind()); + overwrittenVariable = createNewVariable(lir, location.getLIRKind()); + + var changedInstructions = new ArrayList(); + changedInstructions.add(instructions.get(0).getFirst()); + changedInstructions.add(createSymbolSpawnOp(liveVariable, location)); + changedInstructions.add(createSymbolSpawnOp(overwrittenVariable, location)); + changedInstructions.add(createSymbolUsage(overwrittenVariable, location)); + changedInstructions.add(createSymbolUsage(liveVariable, location)); + + for (var blockId : lir.getBlocks()) { + var block = lir.getBlockById(blockId); + instructions.put(block, new ArrayList<>()); + } + + var startBlock = lir.getControlFlowGraph().getStartBlock(); + instructions.put(startBlock, changedInstructions); + } + } + @Override protected CompilationResult compile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, CompilationResult compilationResult, CompilationIdentifier compilationId, OptionValues options) { - // Test cases won't pass if FailOnFirst is false - var newOptions = new OptionValues(options, RegAllocVerifierPhase.Options.RAVFailOnFirst, true); + OptionValues newOptions; + if (disableInlining) { + // Disable any inlining to allow for function calls + newOptions = new OptionValues(options, + RegAllocVerifierPhase.Options.RAVFailOnFirst, true, + HighTier.Options.Inline, false, + BytecodeParserOptions.InlineDuringParsing, false); + } else { + newOptions = new OptionValues(options, RegAllocVerifierPhase.Options.RAVFailOnFirst, true); + } + return super.compile(installedCodeOwner, graph, compilationResult, compilationId, newOptions); } @@ -1208,6 +1268,10 @@ protected LIRSuites createLIRSuites(OptionValues options) { return createModifiedVerifierLIRSuites(options); } + /** + * Create LIR suites with verification enabled, but no tainting. This is done preemptively to + * check that the verification is passing. + */ protected LIRSuites createLIRSuitesWithVerifier(OptionValues options) { LIRSuites suites = super.createLIRSuites(options); var stage = suites.getAllocationStage(); @@ -1235,6 +1299,9 @@ protected LIRSuites createLIRSuitesWithVerifier(OptionValues options) { return suites; } + /** + * Create LIR suites with tainted verifier. + */ protected LIRSuites createModifiedVerifierLIRSuites(OptionValues options) { LIRSuites suites = super.createLIRSuites(options); var stage = suites.getAllocationStage(); @@ -1269,13 +1336,27 @@ protected void compileModified(String name) { validSuites = true; } - protected void assertException(Class expected) { + private T runVerifierExpectingException(String methodName, RAVPhaseWrapper testPhase, Class expectedException) { + phase = testPhase; + + compile(getResolvedJavaMethod(methodName), null); + Assert.assertNull(exception); + + compileModified(methodName); + + return assertException(expectedException); + } + + private T assertException(Class expectedException) { Assert.assertNotNull("No exception was thrown", exception); - if (exception.getCause() != null) { - exception = exception.getCause(); + + RAVException actualException = exception; + if (actualException.getCause() != null) { + actualException = actualException.getCause(); } - Assert.assertTrue("Unexpected exception: " + exception, expected.isInstance(exception)); + Assert.assertTrue("Unexpected exception: " + actualException, expectedException.isInstance(actualException)); + return expectedException.cast(actualException); } public static int simple(int a) { @@ -1389,46 +1470,55 @@ public int referenceSnippet(int a, int b) { return obj.getA(); } + public static int consumeLiveValues(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { + return a + b + c + d + e + f + g + h + i + j; + } + + public static int consumeReloadedValue(int value) { + return 3 * value + 1; + } + + /** + * Increase register pressure so that some variable is spilled and reloaded. + */ + public static int spillAndReload(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n) { + int v0 = a + n; + int v1 = b + m; + int v2 = c + l; + int v3 = d + k; + int v4 = e + j; + int v5 = f + i; + int v6 = g + h; + int v7 = a + c + e; + int v8 = b + d + f; + int v9 = m + n + k; + int v10 = a + g + m; + int v11 = b + h + n; + int kept = v10 ^ v11; + int call = consumeLiveValues(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + int reloaded = consumeReloadedValue(kept); + return call + reloaded + kept + v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11; + } + @Before public void prepareTest() { exception = null; validSuites = true; phase = null; + disableInlining = false; } @Test public void testInvalidRegisterUsed() { var disallowedRegPhase = new DisallowedRegisterPhase(); - phase = disallowedRegPhase; - - var methodName = "simple"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(InvalidRegisterUsedException.class); - var iruException = (InvalidRegisterUsedException) exception; - // Used forbidden register + var iruException = runVerifierExpectingException("simple", disallowedRegPhase, InvalidRegisterUsedException.class); Assert.assertEquals(iruException.register, disallowedRegPhase.ignoredReg); } @Test public void testWrongVariableInState() { var changeVariablePhase = new ChangeVariablePhase(); - phase = changeVariablePhase; - - var methodName = "simple"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(ValueNotInRegisterException.class); - - var vnrException = (ValueNotInRegisterException) exception; + var vnrException = runVerifierExpectingException("simple", changeVariablePhase, ValueNotInRegisterException.class); // Expected original variable Assert.assertEquals(changeVariablePhase.originalVariable, vnrException.variable); @@ -1440,139 +1530,59 @@ public void testWrongVariableInState() { @Test public void testUnknownLocation() { var changeLocationPhase = new ForceUnknownStateInRegister(); - phase = changeLocationPhase; - - var methodName = "keep"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(ValueNotInRegisterException.class); - - var vnrException = (ValueNotInRegisterException) exception; + var vnrException = runVerifierExpectingException("keep", changeLocationPhase, ValueNotInRegisterException.class); Assert.assertTrue(vnrException.state.isUnknown()); - Assert.assertEquals(vnrException.variable, changeLocationPhase.variable); - Assert.assertEquals(vnrException.location.getValue(), changeLocationPhase.oldRegister); + Assert.assertEquals(vnrException.variable, changeLocationPhase.symbol); + Assert.assertEquals(vnrException.location, changeLocationPhase.oldLocation); } @Test public void testConflictedLoc() { var diamondConflictPhase = new DiamondConflictPhase(); - phase = diamondConflictPhase; - - var methodName = "diamond"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(ValueNotInRegisterException.class); - var vnrException = (ValueNotInRegisterException) exception; + var vnrException = runVerifierExpectingException("diamond", diamondConflictPhase, ValueNotInRegisterException.class); Assert.assertTrue(vnrException.state.isConflicted()); var confState = (ConflictedAllocationState) vnrException.state; var conflictedStates = confState.getConflictedStates(); - Assert.assertEquals(2, conflictedStates.size()); - for (var state : conflictedStates) { - Assert.assertTrue(state.getRAValue().equals(diamondConflictPhase.newVariable) || state.getRAValue().equals(diamondConflictPhase.targetVariable)); - } + Assert.assertEquals(conflictedStates, diamondConflictPhase.conflictVariables); } @Test public void testConflictInLoop() { var loopConflictPhase = new LoopConflictPhase(); - phase = loopConflictPhase; - - var methodName = "sum"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(ValueNotInRegisterException.class); - var vnrException = (ValueNotInRegisterException) exception; + var vnrException = runVerifierExpectingException("sum", loopConflictPhase, ValueNotInRegisterException.class); Assert.assertTrue(vnrException.state.isConflicted()); var confState = (ConflictedAllocationState) vnrException.state; - var conflictedStates = confState.getConflictedStates(); - Assert.assertEquals(2, conflictedStates.size()); - for (var state : conflictedStates) { - Assert.assertTrue(state.getRAValue().equals(loopConflictPhase.newVariable) || state.getRAValue().equals(loopConflictPhase.targetVariable)); - } + Assert.assertEquals(confState.getConflictedStates(), loopConflictPhase.conflictVariables); } @Test public void testConflictInInfiniteLoop() { var loopConflictPhase = new LoopConflictPhase(); - phase = loopConflictPhase; - - var methodName = "loop"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(ValueNotInRegisterException.class); - var vnrException = (ValueNotInRegisterException) exception; + var vnrException = runVerifierExpectingException("loop", loopConflictPhase, ValueNotInRegisterException.class); Assert.assertTrue(vnrException.state.isConflicted()); var confState = (ConflictedAllocationState) vnrException.state; - var conflictedStates = confState.getConflictedStates(); - Assert.assertEquals(2, conflictedStates.size()); - for (var state : conflictedStates) { - Assert.assertTrue(state.getRAValue().equals(loopConflictPhase.newVariable) || state.getRAValue().equals(loopConflictPhase.targetVariable)); - } + Assert.assertEquals(confState.getConflictedStates(), loopConflictPhase.conflictVariables); } @Test public void testAliveConstraintInDest() { var violateAliveConstraintPhase = new ViolateAliveConstraintInDstPhase(); - phase = violateAliveConstraintPhase; - - var methodName = "arrayLengthProviderSnippet"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(AliveConstraintViolationException.class); + runVerifierExpectingException("arrayLengthProviderSnippet", violateAliveConstraintPhase, AliveConstraintViolationException.class); } @Test public void testAliveConstraintInTemp() { var violateAliveConstraintPhase = new ViolateAliveConstraintInTempPhase(); - phase = violateAliveConstraintPhase; - - var methodName = "aliveConstraintSnippet"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(AliveConstraintViolationException.class); + runVerifierExpectingException("aliveConstraintSnippet", violateAliveConstraintPhase, AliveConstraintViolationException.class); } @Test public void testKindMatchAfterAlloc() { var changeInputKindPhase = new ChangeInputKindPhase(); - phase = changeInputKindPhase; - - var methodName = "keep"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(KindsMismatchException.class); - var kmException = (KindsMismatchException) exception; + var kmException = runVerifierExpectingException("keep", changeInputKindPhase, KindsMismatchException.class); Assert.assertEquals(changeInputKindPhase.variable.index, kmException.value1.asVariable().getVariable().index); Assert.assertEquals(changeInputKindPhase.variable.getValueKind(), kmException.value2.getValue().getValueKind()); Assert.assertEquals(ValueKind.Illegal, kmException.value1.getValue().getValueKind()); @@ -1582,17 +1592,7 @@ public void testKindMatchAfterAlloc() { @Test public void testKindMatchInState() { var changeInputKindPhase = new ChangeOutputKindPhase(); - phase = changeInputKindPhase; - - var methodName = "keep"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(KindsMismatchException.class); - var kmException = (KindsMismatchException) exception; + var kmException = runVerifierExpectingException("keep", changeInputKindPhase, KindsMismatchException.class); Assert.assertEquals(changeInputKindPhase.variable.index, kmException.value1.asVariable().getVariable().index); Assert.assertEquals(changeInputKindPhase.variable.index, kmException.value2.asVariable().getVariable().index); Assert.assertEquals(changeInputKindPhase.variable.getValueKind(), kmException.value1.getValue().getValueKind()); @@ -1603,63 +1603,47 @@ public void testKindMatchInState() { @Test public void testCalleeSaveRetrieval() { var calleeSavePhase = new CalleeSavePhase(); - phase = calleeSavePhase; - - var methodName = "simple"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(CalleeSavedRegisterNotRetrievedException.class); - var csException = (CalleeSavedRegisterNotRetrievedException) exception; + var csException = runVerifierExpectingException("simple", calleeSavePhase, CalleeSavedRegisterNotRetrievedException.class); Assert.assertEquals(csException.register.getRegister(), calleeSavePhase.register); } @Test public void testOperandFlags() { var forceOperandFlagPhase = new ForceOperandFlagPhase(); - phase = forceOperandFlagPhase; - - var methodName = "simple"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(OperandFlagMismatchException.class); + runVerifierExpectingException("simple", forceOperandFlagPhase, OperandFlagMismatchException.class); } @Test public void testMissingLocation() { var forceMissingLocationException = new ForceMissingLocationExceptionPhase(); - phase = forceMissingLocationException; - - var methodName = "simple"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(MissingLocationException.class); + runVerifierExpectingException("simple", forceMissingLocationException, MissingLocationException.class); } @Test public void testExcessReference() { var forceExcessReference = new ExcessReferencePhase(); - phase = forceExcessReference; + runVerifierExpectingException("simple", forceExcessReference, MissingReferenceException.class); + } - var methodName = "simple"; - compile(getResolvedJavaMethod(methodName), null); + @Test + public void testEarlyReuse() { + var earlyReusePhase = new EarlyReusePhase(); + // The phase overwrites the method here + var vnrException = runVerifierExpectingException("simple", earlyReusePhase, ValueNotInRegisterException.class); - Assert.assertNull(exception); + Assert.assertEquals(earlyReusePhase.liveVariable, vnrException.variable); + Assert.assertEquals(earlyReusePhase.location, vnrException.location); + Assert.assertTrue(vnrException.state instanceof ValueAllocationState); + Assert.assertEquals(earlyReusePhase.overwrittenVariable, ((ValueAllocationState) vnrException.state).getRAValue()); + } - compileModified(methodName); + @Test + public void testForgottenReload() { + disableInlining = true; + var forgottenReloadPhase = new ForgottenReloadPhase(); + var vnrException = runVerifierExpectingException("spillAndReload", forgottenReloadPhase, ValueNotInRegisterException.class); - assertException(MissingReferenceException.class); + Assert.assertEquals(vnrException.location, forgottenReloadPhase.expectedReloadLocation); } @Test @@ -1681,36 +1665,15 @@ public void testPhiResolver() { @Test public void testConstRematerializer() { var constRemPhase = new PhantomConstRematerializationPhase(); - phase = constRemPhase; - - var methodName = "simple"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(ConstantRematerializedToStackException.class); - var crException = (ConstantRematerializedToStackException) exception; + var crException = runVerifierExpectingException("simple", constRemPhase, ConstantRematerializedToStackException.class); Assert.assertEquals(constRemPhase.stackSlotValue, crException.location); - Assert.assertEquals(constRemPhase.variableValue, crException.variable); Assert.assertEquals(constRemPhase.constant, crException.state.getValue()); } @Test public void testDeleteReference() { var deleteReferencePhase = new DeleteReferencePhase(); - phase = deleteReferencePhase; - - var methodName = "referenceSnippet"; - compile(getResolvedJavaMethod(methodName), null); - - Assert.assertNull(exception); - - compileModified(methodName); - - assertException(ValueNotInRegisterException.class); - var vnrException = (ValueNotInRegisterException) exception; + var vnrException = runVerifierExpectingException("referenceSnippet", deleteReferencePhase, ValueNotInRegisterException.class); Assert.assertEquals(vnrException.variable, deleteReferencePhase.variable); Assert.assertEquals(vnrException.location, deleteReferencePhase.location); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java index 0fa3fc16f751..b75499274f0a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationState.java @@ -29,7 +29,7 @@ /** * Interface for state concrete location is in, stored in {@link AllocationStateMap}. */ -public abstract class AllocationState { +public abstract class AllocationState implements Cloneable { /** * Get the default allocation state for every location, instead of null, we have * {@link UnknownAllocationState unknown} state. @@ -73,9 +73,7 @@ public boolean isConflicted() { * @param other The other state coming from a predecessor edge * @param otherBlock Which block is other state from * @param block Which state is this state from? - * @return What is the new state the location is in. + * @return New state if the current one needs to be changed, null otherwise. */ public abstract AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block); - - public abstract boolean equals(AllocationState other); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java index 4d2ebffde326..06cef281bf2b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AllocationStateMap.java @@ -24,22 +24,23 @@ */ package jdk.graal.compiler.lir.alloc.verifier; -import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.InvalidRegisterUsedException; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import jdk.vm.ci.meta.ValueKind; import java.util.Map; import java.util.Set; /** - * Mapping between a location and allocation state that stores one of these: - - * {@link UnknownAllocationState unknown} - our null state, nothing was stored yet - - * {@link ValueAllocationState value} - symbol that is stored at said location - - * {@link ConflictedAllocationState conflicted} - set of Values that are supposed to be at same - * location + * Mapping between a location and allocation state that stores one of these: + *
    + *
  • {@link UnknownAllocationState unknown} - our null state, nothing was stored yet
  • + *
  • {@link ValueAllocationState value} - symbol that is stored at said location
  • + *
  • {@link ConflictedAllocationState conflicted} - set of Values that are supposed to be at same + * location
  • + *
* *

* Conflicts are resolved by assigning new {@link ValueAllocationState value} to same location. @@ -49,34 +50,26 @@ *

*/ public class AllocationStateMap { - protected BasicBlock block; + protected final BasicBlock block; /** * Internal map maintaining the mapping. */ - protected Map internalMap; - - /** - * Map of casts for locations that was forced by allocator-inserted move, see - * {@link BlockVerifierState#isMoveKindChange}. - */ - protected Map> castMap; + protected final Map internalMap; /** * Register allocation config describing which registers can be used. */ - protected RegisterAllocationConfig registerAllocationConfig; + protected final RegisterAllocationConfig registerAllocationConfig; public AllocationStateMap(BasicBlock block, RegisterAllocationConfig registerAllocationConfig) { internalMap = new EconomicHashMap<>(); - castMap = new EconomicHashMap<>(); this.block = block; this.registerAllocationConfig = registerAllocationConfig; } public AllocationStateMap(BasicBlock block, AllocationStateMap other) { internalMap = new EconomicHashMap<>(other.internalMap); - castMap = new EconomicHashMap<>(other.castMap); registerAllocationConfig = other.registerAllocationConfig; this.block = block; } @@ -86,15 +79,7 @@ public boolean has(RAValue key) { } public AllocationState get(RAValue key) { - return this.get(key, AllocationState.getDefault()); - } - - public AllocationState get(RAValue key, AllocationState defaultValue) { - var state = internalMap.get(key); - if (state == null) { - return defaultValue; - } - return state; + return this.internalMap.getOrDefault(key, AllocationState.getDefault()); } /** @@ -121,8 +106,11 @@ public void put(RAValue key, AllocationState state, RAVInstruction.Base instruct * @param state State to store */ public void putWithoutRegCheck(RAValue key, AllocationState state) { + if (state.isUnknown()) { + internalMap.remove(key); // Do not propagate unknown further + } + internalMap.put(key, state); - castMap.remove(key); // Always remove the cast when new value is inserted. } /** @@ -167,22 +155,45 @@ public Set getValueLocations(RAValue value) { public boolean mergeWith(AllocationStateMap source) { boolean changed = false; for (var entry : source.internalMap.entrySet()) { - if (!this.internalMap.containsKey(entry.getKey())) { + var location = entry.getKey(); + var incomingState = entry.getValue(); + if (!this.internalMap.containsKey(location)) { + if (incomingState.isUnknown()) { + continue; // Unknown and Unknown can be skipped + } + changed = true; - this.putWithoutRegCheck(entry.getKey(), UnknownAllocationState.INSTANCE); + this.putWithoutRegCheck(location, incomingState.clone()); + continue; } - var currentValue = this.internalMap.get(entry.getKey()); - var result = this.internalMap.get(entry.getKey()).meet(entry.getValue(), source.block, this.block); - if (!currentValue.equals(result)) { + var currentState = this.internalMap.get(location); + var newState = currentState.meet(incomingState, source.block, this.block); + if (newState != null) { changed = true; + + this.putWithoutRegCheck(location, newState); } + } - this.putWithoutRegCheck(entry.getKey(), result); + // Process remaining locations from our map that have not yet been processed. + for (var entry : this.internalMap.entrySet()) { + var location = entry.getKey(); + if (source.internalMap.containsKey(location) || entry.getValue().isUnknown()) { + // Only care about unprocessed locations + continue; + } + + var currentState = entry.getValue(); + var resultState = currentState.meet(UnknownAllocationState.INSTANCE, source.block, this.block); + if (resultState != null) { + changed = true; + + entry.setValue(resultState); + } } - castMap.putAll(source.castMap); // This should not affect the merge logic return changed; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java index 0a6b0dd7965f..7b2bf069167d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/BlockVerifierState.java @@ -32,6 +32,16 @@ import jdk.graal.compiler.lir.CastValue; import jdk.graal.compiler.lir.LIRInstruction; import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.AliveConstraintViolationException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.CalleeSavedRegisterNotRetrievedException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.ConstantRematerializedToStackException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.JavaKindReferenceMismatchException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.KindsMismatchException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.MissingLocationException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.MissingReferenceException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.OperandFlagMismatchException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.ValueNotInRegisterException; import jdk.graal.compiler.lir.dfa.LocationMarker; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.ValueUtil; @@ -49,43 +59,31 @@ public class BlockVerifierState { /** * Map maintaining mapping between locations and their state. */ - public AllocationStateMap values; + public final AllocationStateMap values; /** * Register allocation config we use to check if only allocatable registers are the ones used. */ - protected RegisterAllocationConfig registerAllocationConfig; - - /** - * Conflict resolver for constant materialization. - */ - protected ConflictResolver conflictConstantResolver; + protected final RegisterAllocationConfig registerAllocationConfig; /** * Block this state pertains to. */ - protected BasicBlock block; - - protected VariableSynonymMap synonymMap; + protected final BasicBlock block; - protected CalleeSaveMap calleeSaveMap; + protected final CalleeSaveMap calleeSaveMap; public BlockVerifierState(BasicBlock block, RegisterAllocationConfig registerAllocationConfig, - ConflictResolver constantConflictResolver, VariableSynonymMap synonymMap, CalleeSaveMap calleeSaveMap) { this.values = new AllocationStateMap(block, registerAllocationConfig); this.registerAllocationConfig = registerAllocationConfig; - this.conflictConstantResolver = constantConflictResolver; - this.synonymMap = synonymMap; this.calleeSaveMap = calleeSaveMap; this.block = block; } - protected BlockVerifierState(BasicBlock block, BlockVerifierState other) { + public BlockVerifierState(BasicBlock block, BlockVerifierState other) { this.registerAllocationConfig = other.registerAllocationConfig; - this.conflictConstantResolver = other.conflictConstantResolver; this.values = new AllocationStateMap(block, other.values); - this.synonymMap = other.synonymMap; this.calleeSaveMap = other.calleeSaveMap; this.block = block; } @@ -95,8 +93,8 @@ public AllocationStateMap getValues() { } /** - * Merge states of block and it's predecessor. This process creates a new state based on the - * contents of the predecessor, creating conflicts where current locations do not match. + * Merge states of block and it's predecessor. This process modifies the current state based on + * the contents of the predecessor, creating conflicts where current locations do not match. * * @param other Predecessor of this block * @return Was this state changed? @@ -114,8 +112,10 @@ public boolean meetWith(BlockVerifierState other) { */ protected void checkStateValues(RAVInstruction.Op op) { if (!op.hasCompleteState()) { - // Some values are null after allocation because of stack slot allocator - // because it is skipped when iteration (StackLockValue). + /* + * Some values are null after allocation because of stack slot allocator because it is + * skipped when iteration (StackLockValue). + */ return; } @@ -154,9 +154,10 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { if (curr == null) { if (op.isJump()) { - // This can happen if a variable without a usage is passed in - // even when this variable acts as an alias to the next label, - // there's no usage, so no location. + /* + * This can happen if a variable without a usage is passed in even when this + * variable acts as an alias to the next label, there's no usage, so no location. + */ return; } @@ -164,32 +165,32 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } ValueKind currKind = curr.getValue().getValueKind(); - if (values.castMap.containsKey(curr)) { - // The current location might have been cast by a previous move - // see isMoveKindChange comment - currKind = values.castMap.get(curr); - } - - if (!kindsEqual(orig.getValue().getValueKind(), currKind)) { + if (!kindsEqualBetweenPreAndPostAlloc(orig.getValue().getValueKind(), currKind)) { throw new KindsMismatchException(op, block, orig, curr, true); } AllocationState state = this.values.get(curr); if (orig.equals(curr)) { - // For these cases we do not consider checking state taking the original - // register as a symbol, because there are too many cases when this does - // not work, for example, RETURN with rax tends to contain the actual - // generated variable instead of rax symbol, or NEAR_FOREIGN_CALL - // keeps its own registers before and after allocation, but those - // can also contain different variable symbols. + /* + * Whenever both the original symbol and current location are equal, we do not do any + * further checking, as there is no symbol to check. This happens for instructions like + * function calls, which have their inputs set to concrete locations before allocation. + * + * The state here can be unknown, some value or in conflict. + */ return; } if (ValueUtil.isStackSlot(curr.getValue()) && LIRValueUtil.isVirtualStackSlot(orig.getValue())) { - // TestCase: IntegerDivRemCanonicalizationTest - // instruction r10|QWORD = STACKLEA slot: stack:80|ILLEGAL[*] in B0 - // had vstack:0, which is not mentioned in first label or elsewhere - // so symbol vstack:0 won't be found + /* + * For the same reason as a previous statement, if vstack slot was present before + * allocation and stack allocator have run, then we need to check for a vstack and stack + * combination. Both are considered locations, and so we do not have a symbol to check + * for. + * + * Test case IntegerDivRemCanonicalizationTest, has instruction: r10|QWORD = STACKLEA + * slot: stack:80|ILLEGAL[*] in B0, where vstack:0 was present before stack allocation. + */ return; } @@ -198,59 +199,47 @@ protected void checkOperand(RAValue orig, RAValue curr, RAVInstruction.Op op) { } if (state.isConflicted()) { - if (orig.isVariable()) { - var variable = orig.asVariable(); - var confState = (ConflictedAllocationState) state; - - ValueAllocationState resolvedState = this.conflictConstantResolver.resolveConflictedState(variable, confState, curr); - if (resolvedState == null) { - resolvedState = this.synonymMap.resolveConflictedState(variable, confState, curr); - } - - if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { - this.values.put(curr, resolvedState, op); - return; - } - } - throw new ValueNotInRegisterException(op, block, orig, curr, state, this); } if (state instanceof ValueAllocationState valAllocState) { if (!valAllocState.value.equals(orig)) { - if (orig.isVariable()) { - var variable = orig.asVariable(); - - ValueAllocationState resolvedState = null; - if (LIRValueUtil.isConstantValue(valAllocState.value.getValue())) { - resolvedState = this.conflictConstantResolver.resolveValueState(variable, valAllocState, curr); - } else if (valAllocState.getRAValue().isVariable()) { - resolvedState = this.synonymMap.resolveValueState(variable, valAllocState, curr); - } - - if (resolvedState != null && resolvedState.getValue().equals(orig.getValue())) { - this.values.put(curr, resolvedState, op); - return; - } - } - throw new ValueNotInRegisterException(op, block, orig, curr, state, this); } - if (!kindsEqualFromState(orig, valAllocState.value)) { + if (!kindsEqualFromState(orig, valAllocState)) { throw new KindsMismatchException(op, block, orig, valAllocState.value, false); } + if (orig.isConstant()) { + var constant = orig.asConstant(); + checkMaterializationLocation(constant, valAllocState); + } + return; } throw GraalError.shouldNotReachHere("Invalid state " + state); } - protected boolean kindsEqual(RAValue orig, RAValue curr) { + protected void checkMaterializationLocation(RAVConstant constant, ValueAllocationState state) { + if (constant.canRematerializeToStack) { + return; + } + + var source = state.getSource(); + if (source instanceof RAVInstruction.ValueMove move) { + var location = move.getLocation(); + if (LIRValueUtil.isStackSlotValue(location.getValue())) { + throw new ConstantRematerializedToStackException(constant, move.getLocation(), state); + } + } + } + + protected boolean kindsEqualBetweenPreAndPostAlloc(RAValue orig, RAValue curr) { var origKind = orig.getValue().getValueKind(); var currKind = curr.getValue().getValueKind(); - return kindsEqual(origKind, currKind); + return kindsEqualBetweenPreAndPostAlloc(origKind, currKind); } /** @@ -260,7 +249,7 @@ protected boolean kindsEqual(RAValue orig, RAValue curr) { * @param currInputKind Current location kind * @return Are they equal? */ - protected boolean kindsEqual(ValueKind origInputKind, ValueKind currInputKind) { + protected boolean kindsEqualBetweenPreAndPostAlloc(ValueKind origInputKind, ValueKind currInputKind) { ValueKind origKind; if (origInputKind instanceof LIRKindWithCast castKind) { origKind = castKind.getActualKind(); @@ -270,6 +259,9 @@ protected boolean kindsEqual(ValueKind origInputKind, ValueKind currInputK ValueKind currKind; if (currInputKind instanceof LIRKindWithCast castKind) { + // Original symbol was defined with the actual kind + // that is being cast from with current location, + // so we check kinds against that. currKind = castKind.getActualKind(); } else { currKind = currInputKind; @@ -286,17 +278,17 @@ protected boolean kindsEqual(ValueKind origInputKind, ValueKind currInputK *

* * @param orig Original variable - * @param fromState Value stored in the state of the current location + * @param valAllocState State we are checking against * @return Are they equal? */ - protected boolean kindsEqualFromState(RAValue orig, RAValue fromState) { - ValueKind origKind = orig.getValue().getValueKind(); - ValueKind currKind = fromState.getValue().getValueKind(); + protected boolean kindsEqualFromState(RAValue orig, ValueAllocationState valAllocState) { + LIRKind stateKind = valAllocState.getKind(); + ValueKind origKind = orig.getLIRKind(); if (LIRValueUtil.isCast(orig.getValue())) { origKind = LIRValueUtil.uncast(orig.getValue()).getValueKind(); } - return origKind.equals(currKind); + return origKind.equals(stateKind); } /** @@ -326,8 +318,6 @@ public void check(RAVInstruction.Base instruction) { } } else if (instruction instanceof RAVInstruction.LocationMove move) { checkLocationMoveKinds(move); - } else if (instruction instanceof RAVInstruction.ValueMove move) { - checkValueMoveKinds(move); } } @@ -348,7 +338,7 @@ protected void checkReferences(RAVInstruction.Op op) { continue; // Undefined in branch } - if (!valAllocState.getRAValue().getLIRKind().isValue()) { + if (valAllocState.isReference()) { continue; // State holds a reference } @@ -363,7 +353,7 @@ protected void checkReferences(RAVInstruction.Op op) { } var valAllocState = (ValueAllocationState) state; - if (!valAllocState.getRAValue().getLIRKind().isValue()) { + if (valAllocState.isReference()) { continue; // State holds a reference } @@ -381,77 +371,12 @@ protected void checkLocationMoveKinds(RAVInstruction.LocationMove move) { AllocationState state = this.values.get(move.from); if (state instanceof ValueAllocationState valueAllocationState) { RAValue movedValue = valueAllocationState.getRAValue(); - if (!kindsEqual(movedValue, move.to)) { - if (isMoveKindChange(move, valueAllocationState)) { - // This move changes the kind for destination location - // see isMoveKindChange comment - return; - } - + if (!movedValue.getLIRKind().getPlatformKind().equals(move.to.getLIRKind().getPlatformKind())) { throw new KindsMismatchException(move, block, move.to, movedValue, false); } } } - protected void checkValueMoveKinds(RAVInstruction.ValueMove move) { - if (move instanceof RAVInstruction.VirtualLocationMove) { - // v28|DWORD = MOVE input: rax|BYTE moveKind: DWORD - // this type of instruction that is stripped from final - // LIR is not checked for kinds. - return; - } - - if (!kindsEqual(move.getLocation(), move.variableOrConstant)) { - throw new KindsMismatchException(move, block, move.getLocation(), move.variableOrConstant, false); - } - } - - /** - * Does this move change the ValueKind? This happens for when a derived reference gets changed - * to a normal reference, we make sure that the underlying platform kind is equal and then allow - * change from derived ([.+]) to normal reference ([*]). - * - *

- * Currently only allowed if the moved state is {@link ValueAllocationState}. - *

- * - *

- * The standard setup is as this: - * - *

-     * (v8|QWORD[.+] -> rcx|QWORD[.+]) = ADD (x: rcx|QWORD, y: r8|QWORD[.]) size: QWORD
-     * rdx|QWORD[*] = MOVE input: rcx|QWORD[.+] moveKind: QWORD // MoveResolver resolve mapping
-     * JUMP ~outgoingValues: [v8|QWORD[.+] -> rdx|QWORD[*]] destination: B1 -> B3 isThreadedJump: false
-     * 
- * - * Add calculates the derived reference address, move casts it to LIRKind ref, and value is used - * in the JUMP to the next block, where pre-allocation symbol (variable) has derived ref, but - * location has reference. - *

- * - * This is then changed in {@link BlockVerifierState#checkOperand} to verify that the kind is - * correct. - * - * @param move Move that facilitates the change - * @param state Current value state this happens for - * @return if this move changes the kinds - */ - protected boolean isMoveKindChange(RAVInstruction.LocationMove move, ValueAllocationState state) { - var moveValueKind = state.getRAValue().getLIRKind(); - var toKind = move.to.getLIRKind(); - var fromKind = move.from.getLIRKind(); - - if (!moveValueKind.getPlatformKind().equals(toKind.getPlatformKind())) { - return false; - } - - if (!moveValueKind.getPlatformKind().equals(fromKind.getPlatformKind())) { - return false; - } - - return moveValueKind.isDerivedReference() && !toKind.isValue() && fromKind.isDerivedReference(); - } - /** * Check {@link BytecodeFrame frames}, before and after allocation, mainly checking that * {@link LIRKind} is a reference when {@link JavaKind} is an Object and wise-versa, checking @@ -475,8 +400,10 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { var kind = frame.kinds[i]; if (JavaKind.Long.equals(kind)) { - // Skipping long(s) because it can be a numeric value - // or a derived reference / native pointer + /* + * Skipping long(s) because it can be a numeric value or a derived reference / + * native pointer + */ continue; } @@ -487,7 +414,7 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { continue; } - throw new RAVException(orig + " -> " + curr + " not an object java kind when marked as a reference", op, block); + throw new JavaKindReferenceMismatchException(orig, curr, kind, op, block); } else { if (origLIRKind.isValue() && currLIRKind.isValue()) { // Either not a reference or a derived one - which might not be marked as @@ -495,7 +422,7 @@ public void checkBytecodeFrames(RAVInstruction.Op op) { continue; } - throw new RAVException(orig + " -> " + curr + " is a reference when not marked as an object java kind", op, block); + throw new JavaKindReferenceMismatchException(orig, curr, kind, op, block); } } } @@ -513,7 +440,7 @@ protected void checkTempKind(RAVInstruction.Op op) { var curr = op.temp.curr[i]; var orig = op.temp.orig[i]; - if (!kindsEqual(orig, curr)) { + if (!kindsEqualBetweenPreAndPostAlloc(orig, curr)) { // Make sure the assigned register has the correct kind for temp. throw new KindsMismatchException(op, block, orig, curr, true); } @@ -615,19 +542,18 @@ public void updateWithLocationMove(RAVInstruction.LocationMove move) { } var state = this.values.get(move.from); + if (state instanceof ValueAllocationState valueAllocationState) { + var movedValue = valueAllocationState.getRAValue(); + if (!movedValue.getLIRKind().equals(move.to.getLIRKind())) { + state = new ValueAllocationState(valueAllocationState, move.to.getLIRKind()); + } + } if (move.validateRegisters) { this.values.putClone(move.to, state, move); } else { this.values.putWithoutRegCheck(move.to, state.clone()); } - - if (state instanceof ValueAllocationState valueAllocationState) { - var movedValue = valueAllocationState.getRAValue(); - if (!kindsEqual(movedValue, move.to) && isMoveKindChange(move, valueAllocationState)) { - this.values.castMap.put(move.to, move.from.getLIRKind()); // Add a new cast - } - } } /** @@ -639,19 +565,45 @@ public void updateWithLocationMove(RAVInstruction.LocationMove move) { */ protected void updateWithOp(RAVInstruction.Op op) { if (op.references != null) { - // First we remove unknown references, - // then we define new values by the return value + /* + * First we remove unknown references, then we define new values by the return value + */ updateWithSafePoint(op); } if (canCastOpToMove(op)) { - // Moves present before the allocation can also be treated - // same way the one inserted by the allocator + /* + * Moves present before the allocation can also be treated same way the one inserted by + * the allocator + */ RAVInstruction.LocationMove locMove = castMove(op); updateWithLocationMove(locMove); return; } + /* + * For calls, temp lists all registers that are supposed to be caller saved, because + * sometimes there's a difference between RegisterConfig.getCallerSaveRegisters + * + * These registers need to be set to unknown before output registers, in case some of them + * are also set as an output. + */ + for (int i = 0; i < op.temp.count; i++) { + var value = op.temp.curr[i]; + if (value.isIllegal()) { + continue; + } + + // We cannot believe the contents of registers used as temp, thus we need to reset. + RAValue location = op.temp.curr[i]; + + if (op.temp.orig[i].equals(location)) { + this.values.putWithoutRegCheck(location, UnknownAllocationState.INSTANCE); + } else { + this.values.put(location, UnknownAllocationState.INSTANCE, op); + } + } + for (int i = 0; i < op.dests.count; i++) { if (op.dests.orig[i].isIllegal()) { continue; // Safe to ignore when destination is illegal value, not when used. @@ -669,33 +621,17 @@ protected void updateWithOp(RAVInstruction.Op op) { RAValue variable = op.dests.orig[i]; if (location.equals(variable)) { - // Only check register validity if it was changed by the register allocator, - // for example, rbp is used as input to start block and forbidden to be used by the - // allocator + /* + * Only check register validity if it was changed by the register allocator, for + * example, rbp is used as input to start block and forbidden to be used by the + * allocator + */ this.values.putWithoutRegCheck(location, new ValueAllocationState(variable, op, block)); } else { this.values.put(location, new ValueAllocationState(variable, op, block), op); } } - // For calls, temp lists all registers that are supposed to be caller saved, - // because sometimes there's a difference between RegisterConfig.getCallerSaveRegisters - for (int i = 0; i < op.temp.count; i++) { - var value = op.temp.curr[i]; - if (value.isIllegal()) { - continue; - } - - // We cannot believe the contents of registers used as temp, thus we need to reset. - RAValue location = op.temp.curr[i]; - - if (op.temp.orig[i].equals(location)) { - this.values.putWithoutRegCheck(location, UnknownAllocationState.INSTANCE); - } else { - this.values.put(location, UnknownAllocationState.INSTANCE, op); - } - } - if (op.isLabel() && block.getId() == 0) { updateCalleeSavedRegisters(); } @@ -745,7 +681,7 @@ protected void updateWithSafePoint(RAVInstruction.Op op) { } var valueAllocState = (ValueAllocationState) state; - if (Value.ILLEGAL.equals(valueAllocState.getValue()) || valueAllocState.getValue().getValueKind(LIRKind.class).isValue()) { + if (Value.ILLEGAL.equals(valueAllocState.getValue()) || !valueAllocState.isReference()) { continue; // Not a reference, continue } @@ -753,10 +689,12 @@ protected void updateWithSafePoint(RAVInstruction.Op op) { continue; } - // Remove all references that are not present in the reference list; - // maybe it makes sense to keep registers that have live references, - // that are same as the one in the reference list? Because the list - // is expected to have stack slots and registers can retain the same references. + /* + * Remove all references that are not present in the reference list; maybe it makes + * sense to keep registers that have live references, that are same as the one in the + * reference list? Because the list is expected to have stack slots and registers can + * retain the same references. + */ entry.setValue(new ValueAllocationState(new RAValue(Value.ILLEGAL), op, block)); } } @@ -807,14 +745,16 @@ protected void checkCalleeSavedRegisters() { } for (var reg : registers) { - var regValue = (RARegister) RARegister.create(reg.asValue()); + var regValue = (RAVRegister) RAVRegister.create(reg.asValue()); var state = this.values.get(regValue); if (state instanceof ValueAllocationState valueAllocationState) { var stateValue = valueAllocationState.getRAValue(); var calleeSavedValue = calleeSaveMap.getCalleeSavedValue(regValue); if (stateValue.equals(calleeSavedValue) && stateValue.getLIRKind().equals(calleeSavedValue.getLIRKind())) { - // The same symbol as register means the value was retrieved safely. - // Kinds also need to match + /* + * The same symbol as register means the value was retrieved safely. Kinds also + * need to match + */ continue; } } @@ -831,31 +771,11 @@ protected void checkCalleeSavedRegisters() { * @param valueMove move we update state from */ protected void updateWithValueMove(RAVInstruction.ValueMove valueMove) { - var location = valueMove.getLocation(); - if (location.isVariable()) { - // Moves of this form: - // v4|QWORD[.] = MOVE input: v3|QWORD[.] moveKind: QWORD - // are handled by VariableSynonymMap. - // TestCase: BoxingTest.boxBoolean - return; - } else if (location.isRegister() && valueMove.variableOrConstant.isVariable()) { - var regLoc = location.asRegister(); - - var state = this.values.get(regLoc); - if (state instanceof ValueAllocationState valueAllocationState) { - var value = valueAllocationState.getRAValue(); - if (value instanceof CalleeSaveMap.CalleeSavedRegister) { - // Virtual move in form r1 = VIRTMOVE v1, assigns variable - // v1 to callee saved register; this needs to be saved - // to properly check that callee saved value is retrieved - // at exit point. - calleeSaveMap.addValue(regLoc, valueMove.variableOrConstant.asVariable()); - } - } - - this.values.putWithoutRegCheck(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block)); + var state = new ValueAllocationState(valueMove.constant, valueMove, block); + if (valueMove.validateRegisters) { + this.values.put(valueMove.getLocation(), state, valueMove); } else { - this.values.put(valueMove.getLocation(), new ValueAllocationState(valueMove.variableOrConstant, valueMove, block), valueMove); + this.values.putWithoutRegCheck(valueMove.getLocation(), state); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java index 055f51802961..225ceeef2c75 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSaveMap.java @@ -33,27 +33,35 @@ import jdk.vm.ci.code.RegisterValue; /** - * Make sure callee-saved register values are retrieved at an exit block. + * This class helps with checking that callee-saved register got retrieved at an exit block, in + * {@link BlockVerifierState#checkCalleeSavedRegisters()}. * *

- * Virtual moves assigning to the same registers need to also be handled, because both values could - * be retrieved. + * We save values that are supposed to be retrieved in a map for each callee-saved register. + *

+ * + *

+ * It can be an instance of {@link CalleeSavedRAVRegister} which is just a special class denoting + * that this value is the callee-saved value saved in start block, before first instruction using + * {@link BlockVerifierState#updateCalleeSavedRegisters}. + *

+ * + *

+ * Or it can be a {@link RAValue} that was set by the first label, meaning we are preserving + * parameter that was received. *

*/ public class CalleeSaveMap { - protected RegisterConfig registerConfig; - protected List calleeSaveRegisters; - - protected Map virtualValues; + protected final RegisterConfig registerConfig; + protected final Map virtualValues; public CalleeSaveMap(RegisterConfig registerConfig) { this.registerConfig = registerConfig; - calleeSaveRegisters = registerConfig.getCalleeSaveRegisters(); virtualValues = new EconomicHashMap<>(); } - public class CalleeSavedRegister extends RARegister { - protected CalleeSavedRegister(RegisterValue registerValue) { + public class CalleeSavedRAVRegister extends RAVRegister { + protected CalleeSavedRAVRegister(RegisterValue registerValue) { super(registerValue); } @@ -70,8 +78,8 @@ public String toString() { * @param registerValue Callee saved register * @return Instance of callee saved register */ - public CalleeSavedRegister createCalleeSavedRegister(RegisterValue registerValue) { - var calleeSavedRegister = new CalleeSavedRegister(registerValue); + public CalleeSavedRAVRegister createCalleeSavedRegister(RegisterValue registerValue) { + var calleeSavedRegister = new CalleeSavedRAVRegister(registerValue); virtualValues.put(calleeSavedRegister, calleeSavedRegister); return calleeSavedRegister; } @@ -83,12 +91,12 @@ public CalleeSavedRegister createCalleeSavedRegister(RegisterValue registerValue * @param register Callee saved register * @param value New callee saved value */ - public void addValue(RARegister register, RAValue value) { + public void addValue(RAVRegister register, RAValue value) { if (!isCalleeSaveRegister(register)) { return; } - if (virtualValues.get(register) instanceof CalleeSavedRegister) { + if (virtualValues.get(register) instanceof CalleeSavedRAVRegister) { virtualValues.put(register, value); } } @@ -99,7 +107,7 @@ public void addValue(RARegister register, RAValue value) { * @param register Callee saved register * @return Get callee saved value for this register */ - public RAValue getCalleeSavedValue(RARegister register) { + public RAValue getCalleeSavedValue(RAVRegister register) { return virtualValues.get(register); } @@ -109,7 +117,8 @@ public RAValue getCalleeSavedValue(RARegister register) { * @param register Register * @return true, if register is callee saved */ - public boolean isCalleeSaveRegister(RARegister register) { + public boolean isCalleeSaveRegister(RAVRegister register) { + var calleeSaveRegisters = registerConfig.getCalleeSaveRegisters(); if (calleeSaveRegisters == null) { return false; } @@ -123,6 +132,6 @@ public boolean isCalleeSaveRegister(RARegister register) { * @return callee saved registers */ public List getCalleeSaveRegisters() { - return calleeSaveRegisters; + return registerConfig.getCalleeSaveRegisters(); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java deleted file mode 100644 index 948eb9f80937..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictResolver.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2026, 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.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; - -import java.util.List; - -/** - * Resolve {@link ConflictedAllocationState} occurrences based on an internal set of rules. - * - *

- * In-case comparison of {@link ValueAllocationState} fails, it also might get resolved by this. - *

- */ -public interface ConflictResolver { - /** - * {@link ConflictResolver} can prepare its own internal state here so it can later resolve - * conflicts. - * - * @param lir LIR - * @param blockInstructions IR of the Verifier - */ - void prepare(LIR lir, BlockMap> blockInstructions); - - /** - * This is run during the checking/verification stage, before this any conflict resolution is - * needed. - * - * @param instruction RAV instruction that we build conflict resolver information from - * @param block Block where this instruction is in - */ - void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block); - - /** - * Resolve an issue stemming from {@link ValueAllocationState} not having the correct value in - * verification phase. - * - * @param target Variable we are looking to resolve to - * @param valueState Current ValueAllocationState instance - * @param location Location where the valueState is stored - * @return {@link ValueAllocationState} instance if conflict is resolved, otherwise null. - */ - ValueAllocationState resolveValueState(RAVariable target, ValueAllocationState valueState, RAValue location); - - /** - * Resolve a {@link ConflictedAllocationState} to {@link ValueAllocationState} based on the - * target variable. - * - * @param target Variable we are looking to resolve to - * @param conflictedState Set of conflicted states - * @param location Location where the valueState is stored - * @return {@link ValueAllocationState} instance if conflict is resolved, otherwise null. - */ - ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location); -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java index f09cacf72532..579ba5f5aed3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConflictedAllocationState.java @@ -32,10 +32,10 @@ /** * Conflicted allocation state - two or more instances have collided, and either of them can be * stored at said location, needs to be resolved by either overwriting the location with a new - * {@link ValueAllocationState instance} or by a {@link ConflictResolver} implementation. + * {@link ValueAllocationState instance}. */ public class ConflictedAllocationState extends AllocationState { - protected Set conflictedStates; + protected final Set conflictedStates; public ConflictedAllocationState() { this.conflictedStates = new EconomicHashSet<>(); @@ -61,13 +61,7 @@ public void addConflictedValue(ValueAllocationState state) { } public boolean hasConflictedValue(ValueAllocationState valueAllocationState) { - for (var state : this.conflictedStates) { - if (state.getRAValue().equals(valueAllocationState.getRAValue())) { - return true; - } - } - - return false; + return this.conflictedStates.contains(valueAllocationState); } /** @@ -85,35 +79,33 @@ public boolean isConflicted() { } /** - * Any state coming here will be added to the conflict set and create a new - * {@link ConflictedAllocationState} instance. + * Adds incoming state to conflicted states, does not allocate a new state. * * @param other The other state coming from a predecessor edge - * @return {@link ConflictedAllocationState} with predecessor state added up + * @return always null (see {@link AllocationStateMap#mergeWith}), to not trigger propagation + * through the CFG */ @Override public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { - var newlyConflictedState = new ConflictedAllocationState(this.getConflictedStates()); if (other instanceof ValueAllocationState valueState) { - newlyConflictedState.addConflictedValue(valueState); - } - - if (other instanceof ConflictedAllocationState conflictedState) { + this.addConflictedValue(valueState); + } else if (other instanceof ConflictedAllocationState conflictedState) { for (var otherConfState : conflictedState.getConflictedStates()) { - newlyConflictedState.addConflictedValue(otherConfState); + this.addConflictedValue(otherConfState); } + } else if (other instanceof UnknownAllocationState) { + /* + * Unknown state creates an Illegal ValueAllocationState inside it, because the unknown + * state is coming from a different predecessor to the same block, and it means that + * this location was not defined there, but it was defined in a different predecessor + * block, meaning it's now in a conflicted state, where it either is defined or it - + * should not be used in further blocks. + */ + this.addConflictedValue(ValueAllocationState.createUndefined(otherBlock)); } - if (other instanceof UnknownAllocationState) { - // Unknown state creates an Illegal ValueAllocationState inside it, because - // the unknown state is coming from a different predecessor to the same block, - // and it means that this location was not defined there, but it was defined in a - // different predecessor block, meaning it's now in a conflicted state, where - // it either is defined or it - should not be used in further blocks. - newlyConflictedState.addConflictedValue(ValueAllocationState.createUndefined(otherBlock)); - } - - return newlyConflictedState; + // When conflicted, we do not want to process everything again + return null; } @Override @@ -130,8 +122,13 @@ public AllocationState clone() { * @return Are both states conflicted? */ @Override - public boolean equals(AllocationState other) { - return other.isConflicted(); + public boolean equals(Object other) { + return other instanceof ConflictedAllocationState c && c.isConflicted(); + } + + @Override + public int hashCode() { + return conflictedStates.hashCode(); } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java deleted file mode 100644 index dbf23cc75b38..000000000000 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantMaterializationConflictResolver.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2026, 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.alloc.verifier; - -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.ConstantValue; -import jdk.graal.compiler.lir.LIR; -import jdk.graal.compiler.lir.LIRValueUtil; -import jdk.graal.compiler.lir.StandardOp; -import jdk.graal.compiler.util.EconomicHashMap; -import jdk.graal.compiler.util.EconomicHashSet; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Resolve {@link ConflictedAllocationState conflicts} made by the constant materialization process. - * - *

- * Also checks if the constant can materialize to stack. - *

- */ -public class ConstantMaterializationConflictResolver implements ConflictResolver { - protected Map constantVariableMap; - protected Set canRematerializeToStack; - protected VariableSynonymMap variableSynonymMap; - - public ConstantMaterializationConflictResolver(VariableSynonymMap variableSynonymMap) { - this.constantVariableMap = new EconomicHashMap<>(); - this.canRematerializeToStack = new EconomicHashSet<>(); - this.variableSynonymMap = variableSynonymMap; - } - - /** - * Creates a variable to constant mapping. - * - * @param lir LIR - * @param blockInstructions IR of the Verifier - */ - @Override - public void prepare(LIR lir, BlockMap> blockInstructions) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = blockInstructions.get(block); - - for (var instruction : instructions) { - this.prepareFromInstr(instruction, block); - } - } - } - - /** - * Add a variable to constant mapping from instruction contents. - * - * @param instruction Instruction we are looking at for LoadConstantOp - * @param block Block where instruction is from - */ - @Override - public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { - if (instruction instanceof RAVInstruction.Op op && op.lirInstruction.isLoadConstantOp()) { - var loadConstantOp = StandardOp.LoadConstantOp.asLoadConstantOp(op.lirInstruction); - - if (!op.dests.orig[0].isVariable()) { - return; - } - - var variable = op.dests.orig[0].asVariable(); - var constantValue = new ConstantValue(variable.getValue().getValueKind(), loadConstantOp.getConstant()); - - constantVariableMap.put(variable, constantValue); - if (loadConstantOp.canRematerializeToStack()) { - canRematerializeToStack.add(variable); - } - } - } - - /** - * Resolve conflict of target variable and the constant it represents to the - * {@link ValueAllocationState} of the target variable. - * - * @param target Variable we are looking to resolve to - * @param conflictedState Set of {@link ConflictedAllocationState conflicted states} - * @param location Location where the value is stored - * @return target variable stored in {@link ValueAllocationState} or null. - */ - @Override - public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { - if (!this.constantVariableMap.containsKey(target)) { - var synonym = variableSynonymMap.find(target); - if (this.constantVariableMap.containsKey(synonym) && !synonym.equals(target)) { - var resolvedState = resolveConflictedState(synonym, conflictedState, location); - if (resolvedState != null) { - return new ValueAllocationState(target, resolvedState.getSource(), resolvedState.getBlock()); - } - } - - return null; - } - - var confStates = conflictedState.getConflictedStates(); - - RAVariable variable = null; - ValueAllocationState constantState = null; - - for (var valAllocState : confStates) { - var value = valAllocState.getRAValue(); - if (value.isVariable()) { - if (variable != null && !variable.equals(value)) { - return null; - } - - variable = value.asVariable(); - } else if (LIRValueUtil.isConstantValue(value.getValue())) { - if (constantState != null && !constantState.getRAValue().equals(value)) { - return null; - } - - constantState = valAllocState; - } - } - - if (!target.equals(variable) || constantState == null) { - return null; - } - - if (!this.constantVariableMap.get(variable).equals(constantState.getValue())) { - return null; - } - - if (isRematerializedToWrongLocation(variable, constantState)) { - throw new ConstantRematerializedToStackException(variable, location, constantState); - } - - return new ValueAllocationState(variable, constantState.getSource(), constantState.block); - } - - /** - * Resolve {@link ValueAllocationState} of a constant to the target variable. - * - * @param variable Variable we are looking to resolve to - * @param valueState Current {@link ValueAllocationState} instance - * @param location Location where the value is stored - * @return target variable stored in {@link ValueAllocationState} or null. - */ - @Override - public ValueAllocationState resolveValueState(RAVariable variable, ValueAllocationState valueState, RAValue location) { - if (!this.constantVariableMap.containsKey(variable)) { - var synonym = variableSynonymMap.find(variable); - if (this.constantVariableMap.containsKey(synonym) && !synonym.equals(variable)) { - var resolvedState = resolveValueState(synonym, valueState, location); - if (resolvedState != null) { - return new ValueAllocationState(variable, resolvedState.getSource(), resolvedState.getBlock()); - } - } - - return null; - } - - var stateValue = valueState.getRAValue().getValue(); - if (LIRValueUtil.isConstantValue(stateValue)) { - var constantValue = LIRValueUtil.asConstantValue(stateValue); - if (!this.constantVariableMap.get(variable).equals(constantValue)) { - return null; - } - - if (isRematerializedToWrongLocation(variable, valueState)) { - throw new ConstantRematerializedToStackException(variable, location, valueState); - } - - return new ValueAllocationState(variable, valueState.getSource(), valueState.getBlock()); - } - - return null; - } - - /** - * Check if variable can be rematerialized to said location based on the original instruction - * source, stored in {@link ValueAllocationState}. - * - *

- * Check if the variable cannot rematerialize to stack and if it did so. - *

- * - * @param variable Target variable - * @param state {@link AllocationState state} it is in - * @return Was it rematerialized to a wrong location? - */ - protected boolean isRematerializedToWrongLocation(RAVariable variable, ValueAllocationState state) { - if (canRematerializeToStack.contains(variable)) { - return false; - } - - // Cannot be rematerialized to stack - var source = state.getSource(); - if (source instanceof RAVInstruction.ValueMove move) { - var location = move.getLocation().getValue(); - return LIRValueUtil.isStackSlotValue(location); - } else { - throw new RematerializedConstantSourceMissingError(source, variable); - } - } -} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java index 0b87c6bd676f..e0b25104da62 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/FromUsageResolverGlobal.java @@ -28,10 +28,10 @@ import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.StandardOp; +import jdk.graal.compiler.lir.dfa.UniqueWorkList; import jdk.graal.compiler.util.EconomicHashMap; import jdk.graal.compiler.util.EconomicHashSet; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -67,35 +67,35 @@ public class FromUsageResolverGlobal { /** * LIR of the compilation unit we are resolving for. */ - protected LIR lir; + protected final LIR lir; /** * Verifier IR. */ - protected BlockMap> blockInstructions; + protected final BlockMap> blockInstructions; /** * Mapping of variables to the labels that defined them. */ - public Map labelMap; + public final Map labelMap; /** * Mapping of operation to a set of variable for which this operation is a first usage. */ - public Map> firstUsages; + public final Map> firstUsages; /** * Initial locations of label-defined variables to set them to when their first usage is found. */ - public Map initialLocations; + public final Map initialLocations; /** * Variable and a block where it's coming from (last jump instruction), this variable is aliased * by the succeeding label, which needs to be resolved first, before this one can be. */ class AliasPair { - RAVariable variable; - BasicBlock block; + final RAVariable variable; + final BasicBlock block; AliasPair(RAVariable variable, BasicBlock block) { this.variable = variable; @@ -107,17 +107,17 @@ class AliasPair { * Map of variables are aliases for a list of variables used in predecessor jump instructions. * First, the successor label variable needs to be resolved and after the predecessor labels. */ - private Map> aliasMap; + private final Map> aliasMap; /** * Block map of their usage objects. */ - protected BlockMap blockUsageMap; + protected final BlockMap blockUsageMap; /** * Set of blocks that have no successors. */ - public Set> endBlocks; + public final Set> endBlocks; /** * Information about locations of variables found when traversing LIR, from the first usage, @@ -167,7 +167,7 @@ protected FromUsageResolverGlobal(LIR lir, BlockMap> b * information. */ public void resolvePhiFromUsage() { - Queue> worklist = new ArrayDeque<>(); + Queue> worklist = new UniqueWorkList(lir.getBlocks().length); this.initializeUsages(); @@ -186,7 +186,8 @@ public void resolvePhiFromUsage() { } protected void processBlock(Queue> worklist) { - var block = worklist.remove(); + var block = worklist.poll(); + assert block != null; var exitUsage = blockUsageMap.get(block); exitUsage.processed = true; @@ -223,14 +224,13 @@ protected void processBlock(Queue> worklist) { if (!mergeInto(predReached, usage)) { if (predReached.processed) { continue; - } else if (worklist.contains(pred)) { - continue; } - // Not yet processed, but also not in a worklist - // this can happen when alias has been resolved and - // predecessor block needs to be processed again - // (the processed flag is set to false) + /* + * Either it has not been processed, or the processed flag was set by alias + * resolution (see resolveLabel end) to force block to be processed again to + * resolve another variable. + */ } } @@ -254,7 +254,7 @@ protected boolean mergeInto(BlockUsage block, BlockUsage successor) { } RAValue defValue = successor.locations.get(variable); - if (block.locations.containsKey(variable) && block.locations.get(variable).equals(defValue)) { + if (defValue.equals(block.locations.get(variable))) { continue; } @@ -270,37 +270,12 @@ protected boolean mergeInto(BlockUsage block, BlockUsage successor) { * for the resolution. */ protected void initializeUsages() { - Queue> worklist = new ArrayDeque<>(); - - var startBlock = this.lir.getControlFlowGraph().getStartBlock(); - worklist.add(startBlock); - - // Calculate what is defined when + usages - Set> visited = new EconomicHashSet<>(); - while (!worklist.isEmpty()) { - var block = worklist.poll(); - if (visited.contains(block)) { - continue; - } - - visited.add(block); - + for (var block : lir.getControlFlowGraph().getBlocks()) { var instructions = blockInstructions.get(block); var label = (RAVInstruction.Op) instructions.getFirst(); for (var i = 0; i < label.dests.count; i++) { - if (label.dests.orig[i].isVariable()) { - if (label.dests.curr[i] != null) { - // TestCase: TruffleSafepointTest - // java.concurrent.ForkJoinPool - // some methods for this class have location kept in - // them after the register allocation is complete, - // but such information should be stripped by the allocator. - // This information uses one register for 2 variables in a label - // and triggers an error in the verification - label.dests.curr[i] = null; - } - + if (label.dests.orig[i].isVariable() && label.dests.curr[i] == null) { var variable = label.dests.orig[i].asVariable(); labelMap.put(variable, label); } @@ -350,11 +325,11 @@ protected void initializeUsages() { } if (block.isLoopHeader()) { - // Here we handle loops without any exit that might - // also need a resolution of label variables, but - // are not reachable from endBlocks set, so we - // add predecessors of such loops that are part of the loop - // into the endBlocks set to process them. + /* + * Here we handle loops without any exit that might also need a resolution of label + * variables, but are not reachable from endBlocks set, so we add predecessors of + * such loops that are part of the loop into the endBlocks set to process them. + */ var loop = block.getLoop(); if (loop.getNaturalExits().isEmpty() && loop.getLoopExits().isEmpty()) { var loopBlocks = loop.getBlocks(); @@ -366,12 +341,6 @@ protected void initializeUsages() { } } } - - for (int i = 0; i < block.getSuccessorCount(); i++) { - var succ = block.getSuccessorAt(i); - - worklist.add(succ); - } } } @@ -462,11 +431,25 @@ protected void resolveLabel(BlockUsage usage, RAVInstruction.Op label, BasicBloc var pred = block.getPredecessorAt(j); var jump = (RAVInstruction.Op) blockInstructions.get(pred).getLast(); + var orig = jump.alive.orig[i]; + if (!RAValue.kindsEqual(orig, location)) { + /* + * TestCase: DerivedOopTest
 B3: rdx|QWORD[*] = REGMOVE rcx|QWORD[.+] <--
+                     * Type is cast here [] = JUMP [] [v8|QWORD[.+] -> rdx|QWORD[*]] [] <-- Needs
+                     * the type change B5: [v12|QWORD[*] -> rdx|QWORD[*]] = LABEL [] [] [] [] =
+                     * BLACKHOLE [v12|QWORD[*] -> rdx|QWORD[*]] [] [] 
+ */ + + jump.alive.orig[i] = RAValue.cast(orig, location); + } + jump.alive.curr[i] = location; // Set predecessor location } - // Variables that are passed into jumps without any other usage are resolved - // after variable, it's alias, in successor label. + /* + * Variables that are passed into jumps without any other usage are resolved after + * variable, it's alias, in successor label. + */ if (aliasMap.containsKey(variable)) { var aliasedVariables = aliasMap.get(variable); for (var aliasPair : aliasedVariables) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVConstant.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVConstant.java new file mode 100644 index 000000000000..0754a914f58a --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVConstant.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2026, 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.alloc.verifier; + +import jdk.graal.compiler.lir.ConstantValue; +import jdk.vm.ci.meta.Constant; + +public class RAVConstant extends RAValue { + protected final ConstantValue value; + + /** + * Setting from + * {@link jdk.graal.compiler.lir.StandardOp.LoadConstantOp#canRematerializeToStack}, stored in + * here to be able to check, if it was not violated, if a constant was rematerialized by the + * allocator. + */ + public final boolean canRematerializeToStack; + + public RAVConstant(ConstantValue value, boolean canRematerializeToStack) { + super(value); + + this.value = value; + this.canRematerializeToStack = canRematerializeToStack; + } + + public Constant getConstant() { + return value.getConstant(); + } + + public ConstantValue getConstantValue() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other instanceof RAVConstant ravConstant) { + return value.getConstant().equals(ravConstant.value.getConstant()); + } + return false; + } + + @Override + public int hashCode() { + return value.getConstant().hashCode(); + } + + @Override + public String toString() { + return value.getConstant().toString(); + } + + @Override + public boolean isConstant() { + return true; + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java index 2e2e956fea69..903f95056f78 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVInstruction.java @@ -24,8 +24,10 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.InstructionValueProcedure; import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.VirtualStackSlot; import jdk.vm.ci.code.RegisterValue; @@ -50,18 +52,6 @@ public abstract static class Base { */ protected LIRInstruction lirInstruction; - /** - * List of virtual moves to be inserted after this instruction, virtual moves are ones - * removed by the allocator still holding relevant information to the verification process, - * for example, the first label always uses registers (instead of variables) based on ABI, - * and a move is inserted indicating that those registers have certain variables. - * - *
-         * {@code [rsi, rbp] = LABEL v1 = MOVE rsi}
-         * 
- */ - protected List virtualMoveList; - /** * List of speculative moves, these might be removed but still hold important information * for us, so we add them to the verifier IR in case they are, this happens when a MOVE @@ -72,11 +62,10 @@ public abstract static class Base { * Before alloc: {@code v1 = MOVE v2} after alloc: {@code rax = MOVE rax} *

*/ - protected List speculativeMoveList; + protected List speculativeMoveList; public Base(LIRInstruction lirInstruction) { this.lirInstruction = lirInstruction; - this.virtualMoveList = new ArrayList<>(); this.speculativeMoveList = new ArrayList<>(); } @@ -84,19 +73,11 @@ public LIRInstruction getLIRInstruction() { return lirInstruction; } - public void addVirtualMove(ValueMove virtualMove) { - this.virtualMoveList.add(virtualMove); - } - - public List getVirtualMoveList() { - return virtualMoveList; - } - - public void addSpeculativeMove(ValueMove speculativeMove) { + public void addSpeculativeMove(CoalescedMove speculativeMove) { this.speculativeMoveList.add(speculativeMove); } - public List getSpeculativeMoveList() { + public List getSpeculativeMoveList() { return speculativeMoveList; } } @@ -168,8 +149,10 @@ public RAValue getOrig(int index) { public void addCurrent(int index, Value value) { if (index >= this.count) { - // In test case DerivedOopTest, liveBasePointers has extra - // values after allocation in frame state, so we skip them + /* + * In test case DerivedOopTest, liveBasePointers has extra values after allocation + * in frame state, so we skip them + */ return; } @@ -298,8 +281,10 @@ public Value doValue(LIRInstruction instruction, Value value, LIRInstruction.Ope public Op(LIRInstruction instruction) { super(instruction); - // We first count the number of entries, then allocate - // an array of said size for both current locations and original variables. + /* + * We first count the number of entries, then allocate an array of said size for both + * current locations and original variables. + */ var countValuesProc = new GetCountProcedure(); instruction.forEachInput(countValuesProc); @@ -360,7 +345,29 @@ public String toString() { opString = "OP"; } - return this.dests.toString() + " = " + opString.toUpperCase(Locale.ROOT) + " " + this.uses.toString() + " " + this.alive.toString() + " " + this.temp.toString(); + StringBuilder output = new StringBuilder(); + if (this.dests.count > 0) { + output.append(this.dests); + output.append(" = "); + } + + output.append(opString.toUpperCase(Locale.ROOT)); + if (this.uses.count > 0 || (this.alive.count > 0 || this.temp.count > 0)) { + output.append(" "); + output.append(this.uses); + } + + if (this.alive.count > 0 || this.temp.count > 0) { + output.append(" alive: "); + output.append(this.alive); + } + + if (this.temp.count > 0) { + output.append(" temp: "); + output.append(this.temp); + } + + return output.toString(); } } @@ -493,19 +500,24 @@ public static class ValueMove extends Base { /** * Constant generated by materialization or variable from virtual/speculative move. */ - public RAValue variableOrConstant; + public final RAValue constant; + protected RAValue location; + public final boolean validateRegisters; - protected RAValue location; // Can also be another variable! + public ValueMove(LIRInstruction instr, ConstantValue constant, Value location) { + this(instr, constant, location, true); + } - public ValueMove(LIRInstruction instr, Value variableOrConstant, Value location) { + public ValueMove(LIRInstruction instr, ConstantValue constant, Value location, boolean validateRegisters) { super(instr); - this.variableOrConstant = RAValue.create(variableOrConstant); + this.constant = RAValue.create(constant); this.location = RAValue.create(location); + this.validateRegisters = validateRegisters; } @Override public String toString() { - return getLocation().getValue().toString() + " = VALUEMOVE " + variableOrConstant.getValue().toString(); + return getLocation().getValue().toString() + " = VALUEMOVE " + constant.getValue().toString(); } public void setLocation(RAValue location) { @@ -521,18 +533,19 @@ public RAValue getLocation() { } /** - * Virtual move in from: v28|DWORD = MOVE input: rax|BYTE moveKind: DWORD, where the destination - * is a variable. We flip the relation so that the input register actually stores the - * symbol/variable, which keeps necessary verification information present. + * Move between two variables that has been coalesced into one location, we keep this + * information to replace the destination variable with the source variable, it is never part of + * the verifier IR, so it is not subclass of {@link Base}. */ - public static class VirtualLocationMove extends ValueMove { - public VirtualLocationMove(LIRInstruction instr, Value variableOrConstant, Value location) { - super(instr, variableOrConstant, location); - } + public static class CoalescedMove { + public final LIRInstruction lirInstruction; + public final RAVariable srcVariable; + public final RAVariable dstVariable; - @Override - public String toString() { - return getLocation().getValue().toString() + " = VIRTMOVE " + variableOrConstant.getValue().toString(); + public CoalescedMove(LIRInstruction lirInstruction, Value dstVariable, Value srcVariable) { + this.lirInstruction = lirInstruction; + this.srcVariable = RAValue.create(LIRValueUtil.asVariable(srcVariable)).asVariable(); + this.dstVariable = RAValue.create(dstVariable).asVariable(); } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVRegister.java similarity index 87% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVRegister.java index e0e6a62078fb..914f78f9a40d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RARegister.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVRegister.java @@ -28,12 +28,12 @@ import jdk.vm.ci.code.RegisterValue; /** - * Wrap around {@link RegisterValue} to only index by the name of the {@link Register} it holds. + * Wrap around {@link RegisterValue} to only index by the id of the {@link Register} it holds. */ -public class RARegister extends RAValue { - protected RegisterValue registerValue; +public class RAVRegister extends RAValue { + protected final RegisterValue registerValue; - protected RARegister(RegisterValue registerValue) { + protected RAVRegister(RegisterValue registerValue) { super(registerValue); this.registerValue = registerValue; @@ -48,7 +48,7 @@ public Register getRegister() { } @Override - public RARegister asRegister() { + public RAVRegister asRegister() { return this; } @@ -70,7 +70,7 @@ public int hashCode() { */ @Override public boolean equals(Object other) { - if (other instanceof RARegister otherReg) { + if (other instanceof RAVRegister otherReg) { return this.registerValue.getRegister().equals(otherReg.registerValue.getRegister()); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java index f10c17ae8ba6..c324b8b63c57 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAValue.java @@ -25,9 +25,14 @@ package jdk.graal.compiler.lir.alloc.verifier; import jdk.graal.compiler.core.common.LIRKind; +import jdk.graal.compiler.core.common.LIRKindWithCast; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.ConstantValue; import jdk.graal.compiler.lir.LIRValueUtil; +import jdk.graal.compiler.lir.Variable; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.meta.Value; +import jdk.vm.ci.meta.ValueKind; /** * Wrapper around Value to change how indexing in data structures like {@link java.util.Map} or @@ -38,6 +43,12 @@ * {@link AllocationStateMap} because locations can change kinds and still be associated with one * key/value pair in the map. *

+ * + *

+ * Some value types like {@link RAVConstant constants}, {@link RAVariable variables}, + * {@link RAVRegister registers} have their own subclass, to use them as types for keys and values + * in collections. + *

*/ public class RAValue { /** @@ -52,13 +63,39 @@ public static RAValue create(Value value) { } if (ValueUtil.isRegister(value)) { - return new RARegister(ValueUtil.asRegisterValue(value)); + return new RAVRegister(ValueUtil.asRegisterValue(value)); + } + + if (LIRValueUtil.isConstantValue(value)) { + return new RAVConstant(LIRValueUtil.asConstantValue(value), true); } return new RAValue(value); } - protected Value value; + protected static boolean kindsEqual(RAValue orig, RAValue location) { + var origKind = orig.getLIRKind(); + var currKind = location.getLIRKind(); + if (currKind instanceof LIRKindWithCast castKind) { + currKind = (LIRKind) castKind.getActualKind(); + } + + return origKind.equals(currKind); + } + + public static RAValue cast(RAValue symbol, RAValue kindSrc) { + RAValue castOrig; + if (symbol.isVariable()) { + castOrig = RAValue.create(new Variable(kindSrc.getLIRKind(), symbol.asVariable().getVariable().index)); + } else if (symbol.isConstant()) { + castOrig = RAValue.create(new ConstantValue(kindSrc.getLIRKind(), symbol.asConstant().getConstant())); + } else { + throw new GraalError("should not reach here: cannot cast orig " + symbol); + } + return castOrig; + } + + protected final Value value; protected RAValue(Value value) { this.value = value; @@ -80,18 +117,38 @@ public boolean isVariable() { return false; } + public boolean isConstant() { + return false; + } + + public RAVConstant asConstant() { + return (RAVConstant) this; + } + public boolean isRegister() { return false; } - public RARegister asRegister() { - return (RARegister) this; + public RAVRegister asRegister() { + return (RAVRegister) this; } public LIRKind getLIRKind() { + if (isIllegal() || value.getValueKind().equals(ValueKind.Illegal)) { + return LIRKind.Illegal; + } + return value.getValueKind(LIRKind.class); } + public boolean isLocation() { + return isRegister() || isStackSlot(); + } + + public boolean isStackSlot() { + return LIRValueUtil.isStackSlotValue(value); + } + @Override public int hashCode() { if (LIRValueUtil.isVirtualStackSlot(this.value)) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java index 385967dd38db..5c5b5fb00cfb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVariable.java @@ -35,7 +35,7 @@ *

*/ public class RAVariable extends RAValue { - protected Variable variable; + protected final Variable variable; protected RAVariable(Variable variable) { super(variable); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java index d3c15e1293b6..2396f6b77b10 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ReferencesBuilder.java @@ -24,6 +24,10 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isRegister; +import static jdk.vm.ci.code.ValueUtil.isStackSlot; + import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.lir.LIRFrameState; @@ -40,10 +44,6 @@ import java.util.Map; import java.util.Set; -import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.isRegister; -import static jdk.vm.ci.code.ValueUtil.isStackSlot; - /** * Build references list for operations that can be used to in-validate references that are not part * of it to make sure GC-freed references are not used further. @@ -106,7 +106,7 @@ public int hashCode() { class ReferenceMarker extends LocationMarker { private final List registerAttributes; - protected Map preAllocMap; + protected final Map preAllocMap; protected ReferenceMarker(LIR lir, FrameMap frameMap, Map preAllocMap) { super(lir, frameMap); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java index e3d0d2bf4f86..8c089705b842 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifier.java @@ -28,12 +28,13 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; import jdk.graal.compiler.lir.LIR; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVFailedVerificationException; +import jdk.graal.compiler.lir.dfa.UniqueWorkList; import java.io.OutputStream; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; -import java.util.Queue; /** * Class encapsulating the whole Register Allocation Verification. Maintaining entry states for @@ -44,44 +45,33 @@ public class RegAllocVerifier { /** * Verifier IR that abstracts LIR instructions and marks moves inserted by the allocator. */ - protected BlockMap> blockInstructions; + protected final BlockMap> blockInstructions; /** * State of the block on entry, calculated from its predecessors. */ - protected BlockMap blockEntryStates; + protected final BlockMap blockEntryStates; /** * LIR necessary for to access the program graph. */ - protected LIR lir; + protected final LIR lir; /** * Register Allocator config used for validating if the allocator uses a valid register. */ - protected RegisterAllocationConfig registerAllocationConfig; + protected final RegisterAllocationConfig registerAllocationConfig; /** * Resolves locations for label variables by finding their first usage and walking back to the * defining label. */ - protected FromUsageResolverGlobal fromUsageResolverGlobal; - - /** - * Conflict resolver for re-materialized constants. - */ - protected ConflictResolver constantMaterializationConflictResolver; - - /** - * Conflict resolver for variable synonyms, some virtual moves can be in form vx = MOVE vy, and - * so variables are interchangeable. - */ - protected VariableSynonymMap synonymMap; + protected final FromUsageResolverGlobal fromUsageResolverGlobal; /** * Track callee saved values from start block to exit blocks. */ - protected CalleeSaveMap calleeSaveMap; + protected final CalleeSaveMap calleeSaveMap; public RegAllocVerifier(LIR lir, BlockMap> blockInstructions, RegisterAllocationConfig registerAllocationConfig) { this.lir = lir; @@ -93,9 +83,6 @@ public RegAllocVerifier(LIR lir, BlockMap> blockInstru this.fromUsageResolverGlobal = new FromUsageResolverGlobal(lir, blockInstructions); - this.synonymMap = new VariableSynonymMap(); - this.constantMaterializationConflictResolver = new ConstantMaterializationConflictResolver(this.synonymMap); - this.calleeSaveMap = new CalleeSaveMap(registerAllocationConfig.getRegisterConfig()); } @@ -108,8 +95,8 @@ public RegAllocVerifier(LIR lir, BlockMap> blockInstru * This is necessary to verify instruction inputs correctly. *

*/ - public void calculateEntryBlocks() { - Queue> worklist = new ArrayDeque<>(); + public void computeEntryStates() { + var worklist = new UniqueWorkList(lir.getBlocks().length); var startBlock = this.lir.getControlFlowGraph().getStartBlock(); var startBlockState = createNewBlockState(startBlock); @@ -118,6 +105,10 @@ public void calculateEntryBlocks() { worklist.add(startBlock); while (!worklist.isEmpty()) { var block = worklist.poll(); + if (block.getSuccessorCount() == 0) { + continue; // No entry state to compute for successors + } + var instructions = this.blockInstructions.get(block); // Create new entry state for successor blocks out of current block state @@ -140,13 +131,19 @@ public void calculateEntryBlocks() { } this.blockEntryStates.put(succ, succState); + + /* + * Remove block from the worklist to delay processing this can reduce the number of + * times that merge block is processed due to changes. + */ + worklist.remove(succ); worklist.add(succ); } } } protected BlockVerifierState createNewBlockState(BasicBlock block) { - return new BlockVerifierState(block, registerAllocationConfig, constantMaterializationConflictResolver, synonymMap, calleeSaveMap); + return new BlockVerifierState(block, registerAllocationConfig, calleeSaveMap); } /** @@ -161,9 +158,6 @@ public void verifyInstructionInputs() { var instructions = this.blockInstructions.get(block); for (var instr : instructions) { - this.constantMaterializationConflictResolver.prepareFromInstr(instr, block); - this.synonymMap.prepareFromInstr(instr, block); - state.check(instr); state.update(instr); } @@ -185,9 +179,6 @@ public void verifyInstructionsAndCollectErrors() { var instructions = this.blockInstructions.get(block); for (var instr : instructions) { - this.constantMaterializationConflictResolver.prepareFromInstr(instr, block); - this.synonymMap.prepareFromInstr(instr, block); - try { state.check(instr); state.update(instr); @@ -228,9 +219,7 @@ public void verifyInstructionsAndCollectErrors() { */ @SuppressWarnings("try") public void run(boolean failOnFirst) { - this.fromUsageResolverGlobal.resolvePhiFromUsage(); - - this.calculateEntryBlocks(); + this.computeEntryStates(); if (failOnFirst) { this.verifyInstructionInputs(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 3ba702fbc96a..0e7c71ab87f3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.alloc.RegisterAllocationConfig; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.core.common.cfg.BlockMap; @@ -37,7 +38,10 @@ import jdk.graal.compiler.lir.LIRValueUtil; import jdk.graal.compiler.lir.StandardOp; import jdk.graal.compiler.lir.StateProcedure; +import jdk.graal.compiler.lir.Variable; import jdk.graal.compiler.lir.alloc.RegisterAllocationPhase; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVFailedVerificationException; import jdk.graal.compiler.lir.amd64.AMD64Move; import jdk.graal.compiler.lir.gen.LIRGenerationResult; import jdk.graal.compiler.lir.phases.LIRPhase; @@ -68,15 +72,22 @@ * Needs to extend the RegisterAllocationPhase to not throw an exception. *

*/ +@SuppressWarnings("try") public class RegAllocVerifierPhase extends RegisterAllocationPhase { public static class Options { - @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) public static final OptionKey EnableRAVerifier = new OptionKey<>(true); + // @formatter:off + @Option(help = "Verify that register allocation is indeed, correct", type = OptionType.Debug) + public static final OptionKey EnableRAVerifier = new OptionKey<>(true); - @Option(help = "Verify output of stack allocator with register allocator", type = OptionType.Debug) public static final OptionKey VerifyStackAllocator = new OptionKey<>(true); + @Option(help = "Verify output of stack allocator with register allocator", type = OptionType.Debug) + public static final OptionKey VerifyStackAllocator = new OptionKey<>(true); - @Option(help = "Collect reference map information to verify", type = OptionType.Debug) public static final OptionKey CollectReferences = new OptionKey<>(true); + @Option(help = "Collect reference map information to verify", type = OptionType.Debug) + public static final OptionKey CollectReferences = new OptionKey<>(true); - @Option(help = "Fail on first verification failure", type = OptionType.Debug) public static final OptionKey RAVFailOnFirst = new OptionKey<>(true); + @Option(help = "Fail on first verification failure", type = OptionType.Debug) + public static final OptionKey RAVFailOnFirst = new OptionKey<>(true); + // @formatter:on } /** @@ -91,6 +102,8 @@ public static class Options { private static final TimerKey PreallocTimer = DebugContext.timer("RAV_PreAlloc"); private static final TimerKey VerifierTimer = DebugContext.timer("RAV_Verification"); + private static final TimerKey PhiResolverTimer = DebugContext.timer("RAV_PhiResolver"); + private static final TimerKey AllocationTimer = DebugContext.timer("RAV_Allocation"); public RegAllocVerifierPhase(RegisterAllocationPhase allocator, LIRPhase stackSlotAllocator) { this.allocator = allocator; @@ -143,7 +156,6 @@ public void setStackSlotAllocator(LIRPhase stackSlotAllocator this.stackSlotAllocator = stackSlotAllocator; } - @SuppressWarnings("try") @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { assert allocator != null : "No register allocator present for verification"; @@ -157,25 +169,38 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo boolean verifyStackAlloc = Options.VerifyStackAllocator.getValue(lir.getOptions()); - allocator.apply(target, lirGenRes, context); - if (stackSlotAllocator != null && verifyStackAlloc) { - stackSlotAllocator.apply(target, lirGenRes, context); + try (DebugCloseable t = AllocationTimer.start(lir.getDebug())) { + allocator.apply(target, lirGenRes, context); + if (stackSlotAllocator != null && verifyStackAlloc) { + stackSlotAllocator.apply(target, lirGenRes, context); - if (Options.CollectReferences.getValue(lir.getOptions())) { - // Frame map is only built after stack allocator has run - new ReferencesBuilder().build(lir, lirGenRes.getFrameMap(), preAllocMap); + if (Options.CollectReferences.getValue(lir.getOptions())) { + // Frame map is only built after stack allocator has run + new ReferencesBuilder().build(lir, lirGenRes.getFrameMap(), preAllocMap); + } } } - try (DebugCloseable t = VerifierTimer.start(lir.getDebug())) { - verifyAllocation(lir, preAllocMap, context); - } + verifyAllocation(lir, preAllocMap, context); if (stackSlotAllocator != null && !verifyStackAlloc) { stackSlotAllocator.apply(target, lirGenRes, context); } } + /** + * Which operation generated register as a symbol and the index of it, in the output array. + */ + class OutValue { + int idx; + RAVInstruction.Op op; + + OutValue(int idx, RAVInstruction.Op op) { + this.idx = idx; + this.op = op; + } + } + /** * Save instruction before allocation to keep track of symbols used in instructions. * @@ -188,55 +213,89 @@ protected Map saveInstructionsPreAlloc(LIR BasicBlock block = lir.getBlockById(blockId); ArrayList instructions = lir.getLIRforBlock(block); + Map inputMap = new EconomicHashMap<>(); + Map outputMap = new EconomicHashMap<>(); + RAVInstruction.Base previousInstr = null; for (var instruction : instructions) { - if (this.isVirtualMove(instruction)) { - // Virtual moves (variable = MOV real register) are going to be removed by the - // allocator, but we still need the information about which variables are - // associated with real registers, and so we store them. - // They are generally associated with other instructions - // that's why we append them here to the previous instruction (for example, - // Label - // or Foreign Call) use these, if this instruction was deleted in the allocator, - // then they will be missing too. - assert previousInstr != null; - + boolean outputSpeculative = false; + boolean inputSpeculative = false; + if (instruction.isValueMoveOp()) { var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var location = valueMov.getInput(); - var variable = LIRValueUtil.asVariable(valueMov.getResult()); + var input = valueMov.getInput(); + var result = valueMov.getResult(); + + if (LIRValueUtil.isVariable(result) && isLocation(input)) { + /* + * Speculative move that outputs a new variable from a concrete location. We + * assign the variable to the instruction that uses the same register as + * output (with no symbol). For example, [rsi] = LABEL followed by v0 = MOVE + * rsi, will get mapped to [v0 -> rsi] = LABEL, internally. + */ + var locValue = RAValue.create(input); + var outputValue = outputMap.get(locValue); + if (outputValue != null) { + outputValue.op.dests.orig[outputValue.idx] = RAValue.create(result); + outputMap.remove(locValue); + continue; + } - var virtualMove = new RAVInstruction.VirtualLocationMove(instruction, variable, location); - previousInstr.addVirtualMove(virtualMove); + outputSpeculative = true; + } - // No need to store virtual move here, it is stored into previous instruction. - continue; - } + if (LIRValueUtil.isVariable(input)) { + /* + * Speculative move that has an existing variable as its input, output can + * be either a variable or a concrete location. + * + * If a concrete location is input, then it can be used to assign a symbol + * to an instruction that has none for this register - typically function + * calls. + * + * If it is a variable to variable move, then we save it, in case it was + * coalesced. + * + * For example, rsi = MOVE v1, followed by CALL [rsi], will get mapped to + * CALL [rsi -> v1]. + */ + inputSpeculative = true; + + Variable variable = LIRValueUtil.asVariable(input); + if (isLocation(result)) { + var regKind = result.getValueKind(); + var varKind = input.getValueKind(); + if (!regKind.equals(varKind)) { + variable = new Variable(regKind, variable.index); + } - boolean speculative = false; - if (this.isSpeculativeMove(instruction)) { - speculative = true; - // Speculative moves are in form ry = MOVE vx, which could be removed if - // the variable ends up being allocated to the same register as ry. - // If it was removed, we need to re-add it because it holds - // important information about where the value of this variable - // is placed - for label resolution after the label. - assert previousInstr != null; + // What if type cast information missing? + inputMap.put(RAValue.create(result), RAValue.create(variable)); + } else { + var virtualMove = new RAVInstruction.CoalescedMove(instruction, result, variable); - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var variable = LIRValueUtil.asVariable(valueMov.getInput()); - var register = valueMov.getResult(); + assert previousInstr != null; + previousInstr.addSpeculativeMove(virtualMove); + } + } + } - var virtualMove = new RAVInstruction.ValueMove(instruction, variable, register); - previousInstr.addSpeculativeMove(virtualMove); + if (instruction.isLoadConstantOp()) { + var loadConstOp = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); + var location = RAValue.create(loadConstOp.getResult()); + if (location.isLocation()) { + // Speculative input move that sets variable to concrete location. + var constant = new ConstantValue(loadConstOp.getResult().getValueKind(LIRKind.class), loadConstOp.getConstant()); + inputMap.put(location, RAValue.create(constant)); + } } - var opRAVInstr = new RAVInstruction.Op(instruction); + var op = new RAVInstruction.Op(instruction); - instruction.forEachInput(opRAVInstr.uses.copyOriginalProc); - instruction.forEachOutput(opRAVInstr.dests.copyOriginalProc); - instruction.forEachTemp(opRAVInstr.temp.copyOriginalProc); - instruction.forEachAlive(opRAVInstr.alive.copyOriginalProc); - instruction.forEachState(opRAVInstr.stateValues.copyOriginalProc); + instruction.forEachInput(op.uses.copyOriginalProc); + instruction.forEachOutput(op.dests.copyOriginalProc); + instruction.forEachTemp(op.temp.copyOriginalProc); + instruction.forEachAlive(op.alive.copyOriginalProc); + instruction.forEachState(op.stateValues.copyOriginalProc); instruction.forEachState(new StateProcedure() { @Override public void doState(LIRFrameState state) { @@ -253,22 +312,116 @@ public void doState(LIRFrameState state) { } var values = frame.values.clone(); - opRAVInstr.bcFrames.add(new RAVInstruction.StateValuePair(values, kinds)); + op.bcFrames.add(new RAVInstruction.StateValuePair(values, kinds)); frame = frame.caller(); } } }); - preallocMap.put(instruction, opRAVInstr); + changeOriginalInputToVariable(inputMap, op.uses, outputSpeculative); + changeOriginalInputToVariable(inputMap, op.alive, outputSpeculative); + + preallocMap.put(instruction, op); + + if (!inputSpeculative) { + for (int i = 0; i < op.dests.count; i++) { + var orig = op.dests.orig[i]; + if (orig.isLocation()) { + outputMap.put(orig, new OutValue(i, op)); + } + } - if (!speculative) { - previousInstr = opRAVInstr; + previousInstr = op; } } } + return preallocMap; } + /** + * Changes input of an instruction that is a concrete location (before allocation) to + * variable/constant from input mapping, that is created by speculative moves before this + * instruction. + * + * @param inputMap Map of location to variable assigned from speculative input move + * @param values Input values + * @param outputSpeculative If this input is from a speculative instruction that creates a new + * variable + */ + protected void changeOriginalInputToVariable(Map inputMap, RAVInstruction.ValueArrayPair values, boolean outputSpeculative) { + for (int i = 0; i < values.count; i++) { + var orig = values.orig[i]; + if (!orig.isLocation()) { + continue; + } + + var inputVar = inputMap.get(orig); + if (inputVar == null) { + continue; + } + + values.orig[i] = inputVar; + + if (!outputSpeculative) { + // Do not remove from input map if this operation could be removed. + // This value can be re-used. + inputMap.remove(orig); + } + } + } + + /** + * Normalizes the values in this input array pair to remove any variables that can be + * substituted for constants or variables that are aliased by different ones. + * + *

+ * We do this to make the internal verifier IR more consistent because sometimes a constant + * value can be used as an input, while at other times it's substituted behind a variable, which + * can also re-materialize later. + *

+ * + *

+ * As for variable aliasing, sometimes a move is coalesced but the register allocator, but the + * mentioned of the alias variable still remain; we make sure to remove them. + *

+ * + * @param values Input array pair + * @param synonymMap Variable synonym map, used to find the first concrete variable. + * @param constantMap Constant map, map variables to their respective constant values. + */ + protected void normalizeValues(RAVInstruction.ValueArrayPair values, VariableSynonymMap synonymMap, Map constantMap) { + for (int i = 0; i < values.count; i++) { + var value = values.orig[i]; + if (!value.isVariable()) { + continue; + } + + RAValue substitute; + var variable = value.asVariable(); + if (constantMap.containsKey(variable)) { + substitute = constantMap.get(variable); + } else if (synonymMap.isAliased(variable)) { + var synonym = synonymMap.find(variable); + substitute = constantMap.getOrDefault(synonym, synonym); + } else { + continue; + } + + if (!values.orig[i].getLIRKind().equals(substitute.getLIRKind())) { + /* + * We cast the kind here, because we expect preceeding move to cast the value inside + * the state, for example, rax|QWORD = MOVE rax|QWORD[.] size: QWORD should cast the + * contents of rax to be QWORD, to be consistent with this, we need to change the + * kind here. + */ + substitute = RAValue.cast(substitute, values.orig[i]); + } + + values.orig[i] = substitute; + } + } + /** * Use information before allocation to verify the output of allocator(s). * @@ -278,11 +431,16 @@ public void doState(LIRFrameState state) { */ protected void verifyAllocation(LIR lir, Map preallocMap, AllocationContext context) { var instructions = getVerifierInstructions(lir, preallocMap, context); + var phiResolver = new FromUsageResolverGlobal(lir, instructions); var verifier = new RegAllocVerifier(lir, instructions, getRegisterAllocationConfig(context)); boolean failOnFirst = Options.RAVFailOnFirst.getValue(lir.getOptions()); - try { + try (DebugCloseable t = PhiResolverTimer.start(lir.getDebug())) { + phiResolver.resolvePhiFromUsage(); + } + + try (DebugCloseable t = VerifierTimer.start(lir.getDebug())) { verifier.run(failOnFirst); } catch (RAVException e) { var debugCtx = lir.getDebug(); @@ -338,9 +496,10 @@ protected RegisterAllocationConfig getRegisterAllocationConfig(AllocationContext * @return Verifier IR */ protected BlockMap> getVerifierInstructions(LIR lir, Map preallocMap, AllocationContext ctx) { - Map definedVariables = new EconomicHashMap<>(); - var presentInstructions = preprocessAllocatedInstructions(lir, preallocMap, definedVariables); + VariableSynonymMap synonymMap = new VariableSynonymMap(); + Map constMap = new EconomicHashMap<>(); + var presentInstructions = getPresentInstructionSet(lir); BlockMap> blockInstructions = new BlockMap<>(lir.getControlFlowGraph()); for (var blockId : lir.getBlocks()) { BasicBlock block = lir.getBlockById(blockId); @@ -387,16 +546,37 @@ public void doState(LIRFrameState state) { } }); - instructionList.add(opRAVInstr); - var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); - if (!speculativeMoves.isEmpty()) { - var readdedMoves = handleSpeculativeMoves(opRAVInstr, presentInstructions, definedVariables); - instructionList.addAll(readdedMoves); + normalizeValues(opRAVInstr.uses, synonymMap, constMap); + normalizeValues(opRAVInstr.alive, synonymMap, constMap); + + if (instruction.isLoadConstantOp()) { + var loadConstOp = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); + var location = loadConstOp.getResult(); + + ConstantValue constant = new ConstantValue(location.getValueKind(LIRKind.class), loadConstOp.getConstant()); + + var orig = opRAVInstr.dests.orig[0]; + if (orig.isVariable()) { + var variable = orig.asVariable(); + constMap.put(variable, new RAVConstant(constant, loadConstOp.canRematerializeToStack())); + } + + var curr = opRAVInstr.dests.curr[0]; + boolean validateRegs = !orig.equals(curr); // Only validate actual changes + var valMove = new RAVInstruction.ValueMove(instruction, constant, location, validateRegs); + + instructionList.add(valMove); + } else { + instructionList.add(opRAVInstr); } - var virtualMoves = opRAVInstr.getVirtualMoveList(); - for (var virtMove : virtualMoves) { - instructionList.add(fixOldValueMove(virtMove)); + var speculativeMoves = opRAVInstr.getSpeculativeMoveList(); + for (var move : speculativeMoves) { + if (presentInstructions.contains(move.lirInstruction)) { + continue; + } + + synonymMap.addSynonym(move.srcVariable, move.dstVariable); } } @@ -406,115 +586,24 @@ public void doState(LIRFrameState state) { return blockInstructions; } - /** - * Fixes value move created before any allocation has happened, we mainly care about stack - * allocator running - the old value move still keeps the virtual stack slot, but the underlying - * lir instruction already has an allocated concrete stack slot, so for verification to work - * correctly, it needs to be changed. - * - * @param valueMove Old value move created before any (stack) allocation - * @return Fixed value move - */ - protected RAVInstruction.ValueMove fixOldValueMove(RAVInstruction.ValueMove valueMove) { - if (!LIRValueUtil.isVirtualStackSlot(valueMove.location.getValue())) { - return valueMove; - } - - Value moveLocation; - if (StandardOp.LoadConstantOp.isLoadConstantOp(valueMove.lirInstruction)) { - var loadConstOp = StandardOp.LoadConstantOp.asLoadConstantOp(valueMove.lirInstruction); - moveLocation = loadConstOp.getResult(); - } else { - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(valueMove.lirInstruction); - moveLocation = valueMov.getInput(); - } - - if (ValueUtil.isStackSlot(moveLocation)) { - // Change vstack to allocated stack slot, because it was changed - valueMove.location.value = moveLocation; - } - - return valueMove; - } - /** * Iterate over every instruction after the allocation, save it to a set to see if speculative * moves should be re-added or not and also track if a variable has been defined before. * * @param lir LIR - * @param preallocMap Map of instructions before allocation - * @param definedVariables Output map, set defined variables here * @return Set of instructions present after allocation */ - protected Set preprocessAllocatedInstructions(LIR lir, Map preallocMap, Map definedVariables) { - Set presentInstructions = new EconomicHashSet<>(); + protected Set getPresentInstructionSet(LIR lir) { + Set presentInstructions = new EconomicHashSet<>(Equivalence.IDENTITY); for (var blockId : lir.getBlocks()) { BasicBlock block = lir.getBlockById(blockId); ArrayList instructions = lir.getLIRforBlock(block); - for (var instruction : instructions) { - presentInstructions.add(instruction); - - var rAVInstr = preallocMap.get(instruction); - if (rAVInstr instanceof RAVInstruction.Op op) { - for (int i = 0; i < op.dests.count; i++) { - if (op.dests.orig[i].isVariable()) { - var variable = op.dests.orig[i].asVariable(); - definedVariables.put(variable, op); - } - } - } - } + presentInstructions.addAll(instructions); } return presentInstructions; } - /** - * Handle speculative moves that should be re-added back to the IR to keep verification - * information in-tact, based on instructions present after allocation and variables defined by - * them. - * - * @param op Op that holds these speculative instructions - * @param presentInstructions Instructions present in the IR in the form of a Map - * @param definedVariables Variables already defined - * @return List of speculative moves that need to be added back - */ - protected List handleSpeculativeMoves(RAVInstruction.Op op, Set presentInstructions, Map definedVariables) { - List toAdd = new ArrayList<>(); - for (var speculativeMove : op.getSpeculativeMoveList()) { - if (presentInstructions.contains(speculativeMove.getLIRInstruction())) { - continue; - } - - if (!speculativeMove.getLocation().isVariable() && speculativeMove.variableOrConstant.isVariable()) { - var variable = speculativeMove.variableOrConstant.asVariable(); - var variableDefInstr = definedVariables.get(variable); - if (variableDefInstr == null) { - continue; - } - - if (variableDefInstr.lirInstruction instanceof StandardOp.LabelOp && variableDefInstr.lirInstruction == op.lirInstruction) { - for (int i = 0; i < op.dests.count; i++) { - var orig = op.dests.orig[i]; - if (!orig.isVariable() || op.dests.curr[i] != null) { - continue; - } - - // Add speculative instruction location back to the label - // where it's missing, only when speculative move is part - // of said label. - op.dests.curr[i] = speculativeMove.getLocation(); - } - } - - continue; - } - - toAdd.add(fixOldValueMove(speculativeMove)); - } - return toAdd; - } - /** * Create Register Verifier Instruction that was created by the {@link RegisterAllocationPhase * register allocator}. Generally speaking, it's always a move instruction; other ones return @@ -529,7 +618,7 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) if (instruction.isLoadConstantOp()) { var constatLoad = StandardOp.LoadConstantOp.asLoadConstantOp(instruction); var constant = constatLoad.getConstant(); - var result = constatLoad.getResult(); // Can be RegisterValue or VirtualStackSlot + var result = constatLoad.getResult(); // Concrete location // Constant materialization result return new RAVInstruction.ValueMove(instruction, new ConstantValue(result.getValueKind(), constant), result); @@ -550,7 +639,8 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) return new RAVInstruction.RegMove(instruction, ValueUtil.asRegisterValue(input), ValueUtil.asRegisterValue(result)); } else if (LIRValueUtil.isStackSlotValue(input) && LIRValueUtil.isStackSlotValue(result)) { if (valueMov instanceof AMD64Move.AMD64StackMove stackMove) { - // Cannot access the isScratchAlwaysZero to see if backup slot is used + // Cannot access the isScratchAlwaysZero to see if a backup slot is used. + // We use the backup slot to set the allocation state to unknown. return new RAVInstruction.StackMove(instruction, input, result, stackMove.getBackupSlot()); } @@ -560,48 +650,7 @@ protected RAVInstruction.Base getRAVMoveInstruction(LIRInstruction instruction) return null; } - /** - * Determines if instruction is a virtual {@link RAVInstruction.ValueMove move}, a virtual move - * is a move instruction that moves a real register value into a variable, which is something - * that will always get removed from the final allocated IR. - * - *

- * This information is important to the verification process and needs to be part of the - * Verifier IR. - *

- * - * @param instruction LIR instruction we are looking at - * @return true, if instruction is a virtual move, otherwise false - */ - protected boolean isVirtualMove(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - return false; - } - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var input = valueMov.getInput(); - return (ValueUtil.isRegister(input) || LIRValueUtil.isStackSlotValue(input)) && LIRValueUtil.isVariable(valueMov.getResult()); - } - - /** - * Determines if a {@link RAVInstruction.ValueMove move} is speculative - it could potentially - * be removed, but hold important information to the verification process. - * - *

- * For example, this happens for a move between two variables, and after allocation locations - * are equal, making the move redundant. - *

- * - * @param instruction {@link LIRInstruction instruction} we are looking at - * @return true, if instruction is a speculative move, otherwise false - */ - protected boolean isSpeculativeMove(LIRInstruction instruction) { - if (!instruction.isValueMoveOp()) { - return false; - } - - var valueMov = StandardOp.ValueMoveOp.asValueMoveOp(instruction); - var result = valueMov.getResult(); // Result could be variable or register - return (ValueUtil.isRegister(result) || LIRValueUtil.isVariable(result)) && LIRValueUtil.isVariable(valueMov.getInput()); + protected boolean isLocation(Value value) { + return LIRValueUtil.isStackSlotValue(value) || ValueUtil.isRegister(value); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java index c21df03c3b41..ed4e635cce27 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownAllocationState.java @@ -45,12 +45,12 @@ public boolean isUnknown() { * {@link ConflictedAllocationState conflict} occurs. * * @param other The other state coming from a predecessor edge - * @return {@link UnknownAllocationState Unknown} if both are, otherwise a conflict + * @return null, if both states are Unknown, otherwise a new {@link ConflictedAllocationState} */ @Override public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock block) { if (other.isUnknown()) { - return this; + return null; } if (other instanceof ConflictedAllocationState conflictedState) { @@ -68,10 +68,15 @@ public AllocationState clone() { } @Override - public boolean equals(AllocationState other) { + public boolean equals(Object other) { return other == INSTANCE; } + @Override + public int hashCode() { + return 0; + } + @Override public String toString() { return "Unknown"; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java index 60be3f4b71b2..a52e242c23e0 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/UnknownInstructionError.java @@ -34,8 +34,8 @@ */ @SuppressWarnings("serial") public class UnknownInstructionError extends RAVError { - public LIRInstruction instruction; - public BasicBlock block; + public final LIRInstruction instruction; + public final BasicBlock block; public UnknownInstructionError(LIRInstruction instruction, BasicBlock block) { super(UnknownInstructionError.getErrorMessage(instruction, block)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java index 4e8f4a2baceb..d518b76ebf08 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueAllocationState.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.lir.alloc.verifier; +import jdk.graal.compiler.core.common.LIRKind; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.lir.LIRValueUtil; @@ -35,19 +36,23 @@ * accompanied by {@link RAVInstruction instruction} and {@link BasicBlock block} where it was * created. */ -public class ValueAllocationState extends AllocationState implements Cloneable { - protected RAValue value; - protected RAVInstruction.Base source; - protected BasicBlock block; +public class ValueAllocationState extends AllocationState { + protected final RAValue value; + protected final RAVInstruction.Base source; + protected final BasicBlock block; + + /** + * Kind that this value was cast to, this is done by move instruction. + */ + protected LIRKind castKind; public ValueAllocationState(RAValue raValue, RAVInstruction.Base source, BasicBlock block) { var v = raValue.getValue(); if (ValueUtil.isRegister(v) || LIRValueUtil.isVariable(v) || LIRValueUtil.isConstantValue(v) || LIRValueUtil.isStackSlotValue(v) || Value.ILLEGAL.equals(v)) { - // Here, we make sure that no new value class is used here, without consideration. - - // StackSlot, RegisterValue is present in start block in label as predefined argument - // VirtualStackSlot is used for RESTORE_REGISTERS and SAVE_REGISTERS - // ConstantValue act as Variable + /* + * Making sure only allowed values are flowing to here. Although, the relevant ones are + * only the constant and the variable. + */ this.value = raValue; this.source = source; this.block = block; @@ -60,6 +65,37 @@ public ValueAllocationState(ValueAllocationState other) { this.value = other.getRAValue(); this.source = other.getSource(); this.block = other.getBlock(); + this.castKind = other.castKind; + } + + public ValueAllocationState(ValueAllocationState other, LIRKind castKind) { + this(other); + this.castKind = castKind; + } + + public boolean isCast() { + return castKind != null; + } + + /** + * Get the lir kind of this state, can be cast by a move. + * + * @return Value kind or cast kind + */ + public LIRKind getKind() { + if (isCast()) { + return castKind; + } + + return value.getLIRKind(); + } + + public boolean isReference() { + if (isCast()) { + return !castKind.isValue(); + } + + return !value.getLIRKind().isValue(); } /** @@ -97,8 +133,7 @@ public BasicBlock getBlock() { * @param other The other state coming from a predecessor edge * @param otherBlock Where the other state is coming from * @param currBlock Where the current state is coming from - * @return {@link ValueAllocationState} if their contents are equal, otherwise - * {@link ConflictedAllocationState}. + * @return Newly allocated {@link ConflictedAllocationState} or null, if nothing changed. */ @Override public AllocationState meet(AllocationState other, BasicBlock otherBlock, BasicBlock currBlock) { @@ -122,12 +157,20 @@ public AllocationState meet(AllocationState other, BasicBlock otherBlock, Bas return new ConflictedAllocationState(this, otherValueAllocState); } - return this; + return null; } @Override - public boolean equals(AllocationState other) { - return other instanceof ValueAllocationState otherVal && this.value.equals(otherVal.getRAValue()); + public boolean equals(Object other) { + if (other instanceof ValueAllocationState otherVal) { + if (isUndefinedFromBlock()) { + return otherVal.isUndefinedFromBlock() && otherVal.getBlock().equals(this.block); + } else { + return otherVal.getRAValue().equals(this.value); + } + } + + return false; } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java index 3a127a5e6c37..c08e11df5b79 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VariableSynonymMap.java @@ -24,121 +24,72 @@ */ package jdk.graal.compiler.lir.alloc.verifier; -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.BlockMap; -import jdk.graal.compiler.lir.LIR; import jdk.graal.compiler.util.EconomicHashMap; -import java.util.List; import java.util.Map; /** - * Union-find data structure for synonyms between variables created by virtual moves. + * Simplified disjoint union set implementation. We could on the fact that the destination variable + * is only defined once in variable to variable moves. + * + *

+ * The representative of every union is the original source variable not created by a coalesced + * move. + *

*/ -public class VariableSynonymMap implements ConflictResolver { - protected Map parent; - protected Map rank; +public class VariableSynonymMap { + protected final Map parent; protected VariableSynonymMap() { parent = new EconomicHashMap<>(); - rank = new EconomicHashMap<>(); } - protected void addSynonym(RAVariable source, RAVariable target) { - union(source, target); + /** + * Add a synonym to the disjoint union set. + * + *

+ * The destination variable is only defined once and is always linked to the root of the source + * variable. + *

+ * + *

+ * This method counts on the fact that the source variable was already defined by either a + * different coalesced move or by an existing instruction v2 = MOVE v1 and then + * followed by v1 = MOVE v3 will cause a failure. + *

+ * + * @param src Source variable + * @param dst Destination variable + */ + protected void addSynonym(RAVariable src, RAVariable dst) { + assert !parent.containsKey(dst) : "Cannot redefine variable again."; + + RAVariable rootSrc = find(src); + parent.put(dst, rootSrc); } + /** + * Find the root of the variable. If none is found, then the variable itself is returned. + * + * @param x Variable to find the root of + * @return Root of the variable + */ public RAVariable find(RAVariable x) { parent.putIfAbsent(x, x); - if (!parent.get(x).equals(x)) { - parent.put(x, find(parent.get(x))); - } return parent.get(x); } - protected void union(RAVariable a, RAVariable b) { - RAVariable rootA = find(a); - RAVariable rootB = find(b); - - if (rootA.equals(rootB)) { - return; - } - - int rankA = rank.getOrDefault(rootA, 0); - int rankB = rank.getOrDefault(rootB, 0); - - if (rankA < rankB) { - parent.put(rootA, rootB); - } else if (rankA > rankB) { - parent.put(rootB, rootA); - } else { - parent.put(rootB, rootA); - rank.put(rootA, rankA + 1); - } - } - - protected boolean isSynonymOf(RAVariable source, RAVariable target) { - return find(source).equals(find(target)); - } - - @Override - public void prepare(LIR lir, BlockMap> blockInstructions) { - for (var blockId : lir.getBlocks()) { - var block = lir.getBlockById(blockId); - var instructions = blockInstructions.get(block); - - for (var instruction : instructions) { - this.prepareFromInstr(instruction, block); - } - } - } - - @Override - public void prepareFromInstr(RAVInstruction.Base instruction, BasicBlock block) { - if (instruction instanceof RAVInstruction.ValueMove move) { - if (!move.variableOrConstant.isVariable() || !move.getLocation().isVariable()) { - return; - } - - this.addSynonym(move.variableOrConstant.asVariable(), move.getLocation().asVariable()); - } - } - - @Override - public ValueAllocationState resolveValueState(RAVariable target, ValueAllocationState valueState, RAValue location) { - var stateValue = valueState.getRAValue(); - if (!stateValue.isVariable()) { - return null; - } - - if (!isSynonymOf(target, stateValue.asVariable())) { - return null; - } - - return new ValueAllocationState(target, valueState.source, valueState.block); - } - - @Override - public ValueAllocationState resolveConflictedState(RAVariable target, ConflictedAllocationState conflictedState, RAValue location) { - RAVInstruction.Base source = null; - BasicBlock block = null; - - var confStates = conflictedState.getConflictedStates(); - for (var valueState : confStates) { - var stateValue = valueState.getRAValue(); - if (!stateValue.isVariable()) { - return null; - } - - if (!isSynonymOf(target, stateValue.asVariable())) { - return null; - } - - // Currently take any source, but maybe it is better to track the original variable - source = valueState.source; - block = valueState.block; - } - - return new ValueAllocationState(target, source, block); + /** + * Check if a variable is being aliased by some other variable. + * + *

+ * The parent map has to have an entry, and it has to be different from the variable itself. + *

+ * + * @param variable Input variable + * @return Is aliased? + */ + protected boolean isAliased(RAVariable variable) { + return parent.containsKey(variable) && !parent.get(variable).equals(variable); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java index 20ed018ccc72..d9c621275621 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/VerifierPrinter.java @@ -26,6 +26,11 @@ import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.LogStream; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.CalleeSavedRegisterNotRetrievedException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.MissingReferenceException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVFailedVerificationException; +import jdk.graal.compiler.lir.alloc.verifier.exceptions.ValueNotInRegisterException; import java.io.OutputStream; @@ -33,8 +38,8 @@ public class VerifierPrinter { public static int PADDING = 4; public static int INDENT = 4; - protected LogStream out; - protected RegAllocVerifier verifier; + protected final LogStream out; + protected final RegAllocVerifier verifier; protected VerifierPrinter(OutputStream out, RegAllocVerifier verifier) { this.out = new LogStream(out); @@ -69,9 +74,10 @@ public void print() { String lirInstrString; if (instruction.lirInstruction == null) { - // Test cases do this, here we just - // indicate that it's not part of LIR - // and never was. + /* + * Test cases do this, here we just indicate that it's not part of LIR and never + * was. + */ lirInstrString = ""; } else { lirInstrString = instruction.lirInstruction.toString(); @@ -387,7 +393,7 @@ private void printCalleeSavedValues(CalleeSavedRegisterNotRetrievedException exc var registers = verifier.registerAllocationConfig.getRegisterConfig().getCalleeSaveRegisters(); out.adjustIndentation(INDENT); for (var reg : registers) { - var regValue = new RARegister(reg.asValue()); + var regValue = new RAVRegister(reg.asValue()); var state = exception.blockVerifierState.values.get(regValue); printAllocationStateInDetail(regValue, state); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/AliveConstraintViolationException.java similarity index 91% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/AliveConstraintViolationException.java index 284012c15fcf..cfa7f0d76001 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/AliveConstraintViolationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/AliveConstraintViolationException.java @@ -22,9 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; +import jdk.graal.compiler.lir.alloc.verifier.RAValue; /** * Violation of the alive inputs occurred, the same location was marked as alive argument as well as @@ -32,7 +34,7 @@ */ @SuppressWarnings("serial") public class AliveConstraintViolationException extends RAVException { - public RAVInstruction.Op instruction; + public final RAVInstruction.Op instruction; /** * Construct an AliveConstraintViolationException. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/CalleeSavedRegisterNotRetrievedException.java similarity index 78% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/CalleeSavedRegisterNotRetrievedException.java index 1e447b2ee335..fb8860f4f1f9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/CalleeSavedRegisterNotRetrievedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/CalleeSavedRegisterNotRetrievedException.java @@ -22,25 +22,27 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.BlockVerifierState; +import jdk.graal.compiler.lir.alloc.verifier.RAVRegister; /** * Callee-saved register was not retrieved on an exit block. */ @SuppressWarnings("serial") public class CalleeSavedRegisterNotRetrievedException extends RAVException { - public RARegister register; - public BlockVerifierState blockVerifierState; + public final RAVRegister register; + public final BlockVerifierState blockVerifierState; - public CalleeSavedRegisterNotRetrievedException(RARegister register, BasicBlock block, BlockVerifierState blockVerifierState) { + public CalleeSavedRegisterNotRetrievedException(RAVRegister register, BasicBlock block, BlockVerifierState blockVerifierState) { super(getErrorMessage(register), null, block); this.register = register; this.blockVerifierState = new BlockVerifierState(block, blockVerifierState); } - public static String getErrorMessage(RARegister register) { + public static String getErrorMessage(RAVRegister register) { return "Callee saved register " + register + " not retrieved on exit"; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantRematerializedToStackException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/ConstantRematerializedToStackException.java similarity index 73% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantRematerializedToStackException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/ConstantRematerializedToStackException.java index c2e4fa5707a2..7a28da50bfa3 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ConstantRematerializedToStackException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/ConstantRematerializedToStackException.java @@ -22,21 +22,25 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; + +import jdk.graal.compiler.lir.alloc.verifier.RAVConstant; +import jdk.graal.compiler.lir.alloc.verifier.RAValue; +import jdk.graal.compiler.lir.alloc.verifier.ValueAllocationState; /** * Constant was rematerialized to a stack location, which was forbidden for this variable/constant. */ @SuppressWarnings("serial") public class ConstantRematerializedToStackException extends RAVException { - public RAVariable variable; - public RAValue location; - public ValueAllocationState state; + public final RAVConstant constant; + public final RAValue location; + public final ValueAllocationState state; - public ConstantRematerializedToStackException(RAVariable variable, RAValue location, ValueAllocationState state) { - super("Variable " + variable + " cannot be rematerialized to stack location " + location, state.getSource(), state.getBlock()); + public ConstantRematerializedToStackException(RAVConstant constant, RAValue location, ValueAllocationState state) { + super("Constant " + constant + " cannot be rematerialized to stack location " + location, state.getSource(), state.getBlock()); - this.variable = variable; + this.constant = constant; this.location = location; this.state = state; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/InvalidRegisterUsedException.java similarity index 92% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/InvalidRegisterUsedException.java index f27944ea67bf..c166c99040d7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/InvalidRegisterUsedException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/InvalidRegisterUsedException.java @@ -22,9 +22,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; import jdk.vm.ci.code.Register; /** @@ -33,7 +34,7 @@ */ @SuppressWarnings("serial") public class InvalidRegisterUsedException extends RAVException { - public Register register; + public final Register register; public InvalidRegisterUsedException(Register register, RAVInstruction.Base instruction, BasicBlock block) { super(getErrorMessage(register), instruction, block); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/JavaKindReferenceMismatchException.java similarity index 53% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/JavaKindReferenceMismatchException.java index 2ce49d11b01f..17d271cfbd2c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RematerializedConstantSourceMissingError.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/JavaKindReferenceMismatchException.java @@ -22,23 +22,31 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.JavaKind; -/** - * Re-materialized constant has a wrong source (not a {@link RAVInstruction.ValueMove}), but either - * undefined or some different Operation or Move. - */ @SuppressWarnings("serial") -public class RematerializedConstantSourceMissingError extends RAVError { - public RematerializedConstantSourceMissingError(RAVInstruction.Base instruction, RAVariable variable) { - super(getErrorMessage(instruction, variable)); +public class JavaKindReferenceMismatchException extends RAVException { + public final AllocatableValue orig; + public final AllocatableValue curr; + public final JavaKind kind; + + public JavaKindReferenceMismatchException(AllocatableValue orig, AllocatableValue curr, JavaKind kind, RAVInstruction.Base instruction, BasicBlock block) { + super(getMessage(orig, curr, kind), instruction, block); + this.orig = orig; + this.curr = curr; + this.kind = kind; } - public static String getErrorMessage(RAVInstruction.Base instruction, RAVariable variable) { - if (instruction == null) { - return "Variable " + variable + " has no materialization source."; + public static String getMessage(AllocatableValue orig, AllocatableValue curr, JavaKind kind) { + if (JavaKind.Object.equals(kind)) { + return orig + " -> " + curr + " not an object java kind when marked as a reference"; + } else { + return orig + " -> " + curr + " is a reference but not marked as a reference"; } - - return "Variable " + variable + " was materialized to " + instruction; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/KindsMismatchException.java similarity index 90% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/KindsMismatchException.java index fb352bdb75dd..bff25656e3cb 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/KindsMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/KindsMismatchException.java @@ -22,18 +22,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; +import jdk.graal.compiler.lir.alloc.verifier.RAValue; /** * Kinds are not matching between two values. */ @SuppressWarnings("serial") public class KindsMismatchException extends RAVException { - public RAValue value1; - public RAValue value2; - public boolean origVsCurr; + public final RAValue value1; + public final RAValue value2; + public final boolean origVsCurr; /** * Construct a KindsMismatchException. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/MissingLocationException.java similarity index 92% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/MissingLocationException.java index 1fb845ee0b26..dac8418d1c74 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingLocationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/MissingLocationException.java @@ -22,9 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; +import jdk.graal.compiler.lir.alloc.verifier.RAValue; /** * No location found in an instruction after allocation for certain variable. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/MissingReferenceException.java similarity index 80% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/MissingReferenceException.java index 332b01e9b1d2..67f73fb3ed9b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/MissingReferenceException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/MissingReferenceException.java @@ -22,16 +22,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.AllocationState; +import jdk.graal.compiler.lir.alloc.verifier.BlockVerifierState; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; +import jdk.graal.compiler.lir.alloc.verifier.RAValue; @SuppressWarnings("serial") public class MissingReferenceException extends RAVException { - public RAValue reference; - public AllocationState state; - public RAVInstruction.Op instruction; - public BlockVerifierState blockVerifierState; + public final RAValue reference; + public final AllocationState state; + public final RAVInstruction.Op instruction; + public final BlockVerifierState blockVerifierState; public MissingReferenceException(RAVInstruction.Op instruction, BasicBlock block, RAValue reference, AllocationState state, BlockVerifierState blockVerifierState) { super(getMessage(reference, state), instruction, block); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/OperandFlagMismatchException.java similarity index 89% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/OperandFlagMismatchException.java index cff94241c42f..5c09badad475 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/OperandFlagMismatchException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/OperandFlagMismatchException.java @@ -22,10 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.lir.LIRInstruction; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; import jdk.vm.ci.meta.Value; import java.util.EnumSet; @@ -37,9 +38,9 @@ */ @SuppressWarnings("serial") public class OperandFlagMismatchException extends RAVException { - public RAVInstruction.Op instruction; - public Value value; - public EnumSet flags; + public final RAVInstruction.Op instruction; + public final Value value; + public final EnumSet flags; public OperandFlagMismatchException(RAVInstruction.Op op, BasicBlock block, Value value, EnumSet flags) { super(getErrorMessage(value, flags), op, block); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/RAVException.java similarity index 91% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/RAVException.java index fdeaae13dfab..d0db7688f1a5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/RAVException.java @@ -22,10 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; /** * Register Allocation Verification Exception - a violation made by the @@ -34,8 +35,8 @@ */ @SuppressWarnings("serial") public class RAVException extends GraalError { - public RAVInstruction.Base instruction; - public BasicBlock block; + public final RAVInstruction.Base instruction; + public final BasicBlock block; public RAVException(String message, RAVInstruction.Base instruction, BasicBlock block) { super(message); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/RAVFailedVerificationException.java similarity index 95% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/RAVFailedVerificationException.java index da79b0d16cc1..1aa67563acff 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RAVFailedVerificationException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/RAVFailedVerificationException.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.debug.GraalError; @@ -35,7 +35,7 @@ */ @SuppressWarnings("serial") public class RAVFailedVerificationException extends GraalError { - public List exceptions; + public final List exceptions; public RAVFailedVerificationException(List exceptions) { super(RAVFailedVerificationException.getMessage(exceptions)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/ValueNotInRegisterException.java similarity index 84% rename from compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/ValueNotInRegisterException.java index dae5540a0d3b..47b0946b4bba 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/ValueNotInRegisterException.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/exceptions/ValueNotInRegisterException.java @@ -22,16 +22,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.graal.compiler.lir.alloc.verifier; +package jdk.graal.compiler.lir.alloc.verifier.exceptions; import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.lir.alloc.verifier.AllocationState; +import jdk.graal.compiler.lir.alloc.verifier.BlockVerifierState; +import jdk.graal.compiler.lir.alloc.verifier.RAVInstruction; +import jdk.graal.compiler.lir.alloc.verifier.RAValue; /** * Value was not found in the location we needed it in. */ @SuppressWarnings("serial") public class ValueNotInRegisterException extends RAVException { - public RAVInstruction.Op instruction; + public final RAVInstruction.Op instruction; /** * Symbol that was not found at the location. @@ -40,7 +44,7 @@ public class ValueNotInRegisterException extends RAVException { * Can be a constant, variable, or other symbolic value. *

*/ - public RAValue variable; + public final RAValue variable; /** * Location where the symbol was not found. @@ -49,9 +53,9 @@ public class ValueNotInRegisterException extends RAVException { * Can be a register or a (virtual) stack slot. *

*/ - public RAValue location; - public AllocationState state; - public BlockVerifierState blockVerifierState; + public final RAValue location; + public final AllocationState state; + public final BlockVerifierState blockVerifierState; /** * Construct a ValueNotInRegisterException. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/package-info.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/package-info.java new file mode 100644 index 000000000000..2eb6eb517ed0 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/package-info.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2026, 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. + */ + +/** + *

Register Allocation Verifier

+ * + * This package verifies the output of the + * {@link jdk.graal.compiler.lir.alloc.RegisterAllocationPhase register allocator} by checking that + * the values flowing into instructions are the same as in the program before allocation. It is + * implemented as a phase in {@link jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase}, + * wrapping around both the register and stack allocators to gain access to both pre- and + * post-allocation LIR. + * + *

The principle

+ * + * {@link jdk.graal.compiler.lir.Variable Variables} and + * {@link jdk.graal.compiler.lir.ConstantValue} from pre-allocation LIR act as symbols that are + * tracked in concrete locations during the verification process, implemented in + * {@link jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifier} and + * {@link jdk.graal.compiler.lir.alloc.verifier.BlockVerifierState}. + * + *

+ * The core algorithm performs symbolic execution on abstract instructions that boil down the + * functionality of LIR instruction to only symbols and locations they read and write, as follows: + *

    + *
  • [(output_symbol1, output_location1), ...] = Op [(input_symbol1, input_location1), ...]
  • + *
  • dst_location = Move src_location
  • + *
+ * + * Where {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.Op Op} is an operation that was + * present before allocation and only had its operands changed. The allocator has to make sure that + * symbols read by the instruction are present at those selected locations. Output symbols are + * written at the output locations, when being symbolically executed. + * {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.LocationMove Move} is an + * allocator-inserted instruction that transfers symbols, created by Op, from source location to the + * destination, keeping the source unchanged. + *

+ * + *

+ * The execution of these instructions goes as follows: + *

    + *
  • Op puts output_symbol(s) to the output_location(s) set by the allocator
  • + *
  • Move puts the symbol from src_location to dst_location
  • + *
+ *

+ * + *

+ * Instead of only maintaining a symbol for a location, an allocation state is stored instead: + *

    + *
  • {@link jdk.graal.compiler.lir.alloc.verifier.UnknownAllocationState Unknown} - no information + * about the contents
  • + *
  • {@link jdk.graal.compiler.lir.alloc.verifier.ValueAllocationState Value(s)} - symbol v is + * stored at this location
  • + *
  • {@link jdk.graal.compiler.lir.alloc.verifier.ConflictedAllocationState Conflict} - + * approximation, multiple symbols can be present
  • + *
+ * + * Every block has its own {@link jdk.graal.compiler.lir.alloc.verifier.AllocationStateMap + * allocation map}: location -> state. Where states are stored for every location. If blocks meet at + * their common successor, two different symbols can be present at the same location. This is + * handled by a {@link jdk.graal.compiler.lir.alloc.verifier.AllocationState#meet} function. The + * logic of the two-state meeting can be described in this way: + * + *
+ * meet(x, x) = x
+ * meet(x, y) = Conflicted, if x != y
+ * 
+ * + * Where both states need to be equal to be passed to the merge block, otherwise a conflict is + * created. Conflict does not mean an error state, rather that it is forbidden to read from this + * location. Conflict can be overwritten by Op generating a new symbol to the same location. + *

+ * + *

+ * The algorithm works in two stages: + *

    + *
  1. Compute the initial state of every block until a fixed point is reached, only generates + * symbols
  2. + *
  3. Verify that read symbols are present at their locations
  4. + *
+ *

+ * + *

+ * {@link jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifier#computeEntryStates} runs a queue of + * basic blocks, starting from the start block. Every block takes its current entry state and runs + * symbolic execution on its instructions. The new state is then passed to its successors. Successor + * is enqueued only if a change was made to its entry state. + *

+ * + *

+ * When every block has a fixed entry state (the allocation map), then we can linearly iterate over + * every block, take its entry state, and for every instruction, first check that read symbols are + * at read locations in the state map, second generate or move symbols. Implemented in + * {@link jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifier#verifyInstructionInputs}. + *

+ * + *

Preprocessing stage and the IR

+ * + * Before the validation process can begin, the intermediate representation needs to be created. + * Pre-allocation instructions are processed and stored in a map: LIRInstruction -> Op. Before + * allocation, we also map symbols to fixed registers, as in this example: + * + *
+ * rdi|DWORD = MOVE input: int[1|0x1]
+ * rsi|QWORD = MOVE input: v7|QWORD moveKind: QWORD
+ * rax = CALL parameters: [rdi|DWORD, rsi|QWORD] temps: [rax|ILLEGAL, ...]
+ * v8 = MOVE input: rax|QWORD
+ * 
+ * + * Maps the symbols 0x1 and v7 to the rdi and rsi in the CALL, so if these moves are coalesced, + * information is not lost. It also maps v8 to the rax output of the CALL. + * + *

+ * After allocation is performed, the IR can now be completed. Every existing lir instruction has a + * {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.Base} counterpart. If it was present + * before allocation, an {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.Op} otherwise a + * {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.LocationMove}. + *

+ * + *

+ * Variable to variable moves, e.g. v2|DWORD = MOVE v1|DWORD can be removed by the + * allocator. If this happens, every occurrence of the output variable is replaced with the source + * variable. {@link jdk.graal.compiler.lir.alloc.verifier.VariableSynonymMap} also handles chained + * coalesced moves, always uses the root source variable created by an existing instruction for + * substitution. + *

+ * + *

Constants

+ * + * Constants are created by {@link jdk.graal.compiler.lir.StandardOp.LoadConstantOp}, where the + * output before allocation can be a variable. They can also be used as operands before allocation + * and be put into a register by the allocator. For example, JUMP [int[0|0x0]] as the initial + * condition of a for loop. + * + *

+ * To normalize this behavior, variables defined by + * {@link jdk.graal.compiler.lir.StandardOp.LoadConstantOp} are substituted for their constant + * values inside the verifier IR. Furthermore, every + * {@link jdk.graal.compiler.lir.StandardOp.LoadConstantOp} has its own instruction + * {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.ValueMove} in the IR, which puts the + * constant symbol into specified location. Doing this also handles constants re-materialized by the + * allocator. + *

+ * + *

Inferring label-defined variable locations

+ * + * The allocator strips information about locations of label-defined variables, as well as locations + * of symbols in the preceding jumps. This information is necessary to correctly verify symbol + * reads. + * + *

+ * This information is reconstructed from the verifier's IR by finding the first usage of the + * label-defined variable and going back through the CFG to the label. Handling any + * allocator-inserted moves that might have moved the symbol. + *

+ * + *

+ * The preceding jumps then assert the result of this reconstruction. Their location matches the one + * in the label. If the symbols flowing through the JUMP into the LABEL are not contained in the + * selected location, for every predecessor, then a failure occurs. + *

+ * + *

+ * Implemented in {@link jdk.graal.compiler.lir.alloc.verifier.FromUsageResolverGlobal}, uses a + * worklist, walking from every end block to the start, resolving label variable locations. + *

+ * + *

Exceptions

+ * + * Exceptions are in the package {@link jdk.graal.compiler.lir.alloc.verifier.exceptions}. Each of + * them stores debugging information about the fault that was detected. Block, instruction where it + * occurred, and other exception-specific information. + * + *

+ * {@link jdk.graal.compiler.lir.alloc.verifier.AllocationState} maintains debug information. + * {@link jdk.graal.compiler.lir.alloc.verifier.ValueAllocationState} contains information about the + * instruction that created the symbol. + * {@link jdk.graal.compiler.lir.alloc.verifier.ConflictedAllocationState} contains all the value + * allocation states that are in conflict. New information added to the set of conflicting value + * states is not propagated through the CFG. + *

+ * + *

+ * When an exception is thrown, a debug file with the extension ".rax.txt" is created in the + * graal_dumps directory. The file contains the CFG and verifier's instructions in a text format. + * Describing in which block and which instruction has the exception occurred. Optionally, the + * process can gather multiple exceptions, always at most one per instruction, using + * {@link jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVFailedVerificationException}. + *

+ * + *

Other checking

+ * + *

+ * Checking that other constraints, the register allocator needs to follow, were not violated. + *

    + *
  • Check kinds between the original symbol and stored symbol
  • + *
  • Check if collected GC roots are actually references
  • + *
  • Check that "alive" inputs live past the instruction
  • + *
  • Check that only allocatable registers are used
  • + *
  • Check if callee-saved registers are retrieved at exit
  • + *
  • Check JavaKind and LIRKind correspondence in {@link jdk.vm.ci.code.BytecodeFrame}
  • + *
  • Check for operand flags
  • + *
+ *

+ * + *

Options

+ *
    + *
  • {@link jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase.Options#EnableRAVerifier} + * - enable this process during compilation
  • + *
  • {@link jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase.Options#VerifyStackAllocator} + * - verify the output of stack allocator
  • + *
  • {@link jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase.Options#CollectReferences} + * - use {@link jdk.graal.compiler.lir.dfa.LocationMarker} interface to collect reference + * information
  • + *
  • {@link jdk.graal.compiler.lir.alloc.verifier.RegAllocVerifierPhase.Options#RAVFailOnFirst} - + * if set to true, first failure is thrown; otherwise all failures are collected at thrown at once + * using + * {@link jdk.graal.compiler.lir.alloc.verifier.exceptions.RAVFailedVerificationException}
  • + *
+ * + *

Collect references

+ * + * The verifier uses {@link jdk.graal.compiler.lir.dfa.LocationMarker} to collect live object + * reference information before {@link jdk.graal.compiler.lir.phases.FinalCodeAnalysisStage final + * code analysis} runs. {@link jdk.graal.compiler.lir.alloc.verifier.RAVInstruction.Op Op} holds a + * set of references. When being symbolically executed + * ({@link jdk.graal.compiler.lir.alloc.verifier.BlockVerifierState#update} and the reference set is + * not null, all references that are not part of the set are deleted from the + * {@link jdk.graal.compiler.lir.alloc.verifier.AllocationStateMap allocation map}. Checking stage, + * then verifies that all references in the set are actually references. + */ +package jdk.graal.compiler.lir.alloc.verifier; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/dfa/UniqueWorkList.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/dfa/UniqueWorkList.java index ec9a11ab5809..a422142380ef 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/dfa/UniqueWorkList.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/dfa/UniqueWorkList.java @@ -34,11 +34,11 @@ * Ensures that an element is only in the worklist once. * */ -class UniqueWorkList extends ArrayDeque> { +public class UniqueWorkList extends ArrayDeque> { private static final long serialVersionUID = 8009554570990975712L; BitSet valid; - UniqueWorkList(int size) { + public UniqueWorkList(int size) { this.valid = new BitSet(size); } @@ -72,4 +72,19 @@ public boolean addAll(Collection> collection) { } return changed; } + + @Override + public boolean contains(Object o) { + return valid.get(((BasicBlock) o).getId()); + } + + @Override + public boolean remove(Object o) { + if (!contains(o)) { + return false; + } + + valid.set(((BasicBlock) o).getId(), false); + return super.remove(o); + } } From dd401b441ce8a73710e4257ffa7fe0fd2c305418 Mon Sep 17 00:00:00 2001 From: danocmx Date: Wed, 29 Apr 2026 16:29:30 +0200 Subject: [PATCH 112/112] Fix amr related issues --- .../graal/compiler/core/test/RegAllocVerifierTest.java | 8 +++++--- .../lir/alloc/verifier/RegAllocVerifierPhase.java | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java index e80f8a6f66c9..2e4d33a2191d 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/RegAllocVerifierTest.java @@ -730,12 +730,12 @@ class CalleeSavePhase extends RAVPhaseWrapper { @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { - var name = target.arch.getName(); + var name = target.arch.toString(); // Select first paramter register for this switch (name) { - case "AMD64" -> register = AMD64.rsi; - case "ARM64" -> register = AArch64.r0; + case "amd64" -> register = AMD64.rsi; + case "aarch64" -> register = AArch64.r1; } super.run(target, lirGenRes, context); @@ -743,6 +743,8 @@ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Allo @Override protected RegisterAllocationConfig getRegisterAllocationConfig(AllocationContext context) { + assert register != null : "Callee save register not selected"; + var regCfg = context.registerAllocationConfig.getRegisterConfig(); var newRegCfg = new RegisterConfig() { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java index 0e7c71ab87f3..c753cae2d2a6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/verifier/RegAllocVerifierPhase.java @@ -361,7 +361,7 @@ protected void changeOriginalInputToVariable(Map inputMap, RAV continue; } - values.orig[i] = inputVar; + values.orig[i] = RAValue.cast(inputVar, values.orig[i]); if (!outputSpeculative) { // Do not remove from input map if this operation could be removed.