package com.oracle.svm.hosted.code.aarch64;
import java.util.function.Consumer;
import org.graalvm.compiler.asm.Assembler.CodeAnnotation;
import org.graalvm.compiler.asm.aarch64.AArch64Assembler.SingleInstructionAnnotation;
import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler;
import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler.MovSequenceAnnotation.MovAction;
import org.graalvm.compiler.code.CompilationResult;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.Feature;
import com.oracle.objectfile.ObjectFile.RelocationKind;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.graal.code.CGlobalDataReference;
import com.oracle.svm.core.graal.code.PatchConsumerFactory;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.code.HostedPatcher;
import com.oracle.svm.hosted.image.RelocatableBuffer;
import jdk.vm.ci.code.site.ConstantReference;
import jdk.vm.ci.code.site.DataSectionReference;
import jdk.vm.ci.code.site.Reference;
@AutomaticFeature
@Platforms({Platform.AARCH64.class})
public class AArch64HostedPatcher implements Feature {
@Override
public void afterRegistration(AfterRegistrationAccess access) {
ImageSingletons.add(PatchConsumerFactory.HostedPatchConsumerFactory.class, new PatchConsumerFactory.HostedPatchConsumerFactory() {
@Override
public Consumer<CodeAnnotation> newConsumer(CompilationResult compilationResult) {
return new Consumer<CodeAnnotation>() {
@Override
public void accept(CodeAnnotation annotation) {
if (annotation instanceof SingleInstructionAnnotation) {
compilationResult.addAnnotation(new SingleInstructionHostedPatcher((SingleInstructionAnnotation) annotation));
} else if (annotation instanceof AArch64MacroAssembler.MovSequenceAnnotation) {
compilationResult.addAnnotation(new MovSequenceHostedPatcher((AArch64MacroAssembler.MovSequenceAnnotation) annotation));
} else if (annotation instanceof AArch64MacroAssembler.AdrpLdrMacroInstruction) {
compilationResult.addAnnotation(new AdrpLdrMacroInstructionHostedPatcher((AArch64MacroAssembler.AdrpLdrMacroInstruction) annotation));
} else if (annotation instanceof AArch64MacroAssembler.AdrpAddMacroInstruction) {
compilationResult.addAnnotation(new AdrpAddMacroInstructionHostedPatcher((AArch64MacroAssembler.AdrpAddMacroInstruction) annotation));
}
}
};
}
});
}
}
class SingleInstructionHostedPatcher extends CompilationResult.CodeAnnotation implements HostedPatcher {
private final SingleInstructionAnnotation annotation;
SingleInstructionHostedPatcher(SingleInstructionAnnotation annotation) {
super(annotation.instructionPosition);
this.annotation = annotation;
}
@Override
public void relocate(Reference ref, RelocatableBuffer relocs, int compStart) {
throw VMError.shouldNotReachHere("Currently relocations must use either adrp/ldp, adrp/add, or a sequence of moves");
}
@Uninterruptible(reason = ".")
@Override
public void patch(int codePos, int relative, byte[] code) {
annotation.patch(codePos, relative, code);
}
@Override
public boolean equals(Object obj) {
return obj == this;
}
}
class AdrpLdrMacroInstructionHostedPatcher extends CompilationResult.CodeAnnotation implements HostedPatcher {
private final AArch64MacroAssembler.AdrpLdrMacroInstruction macroInstruction;
AdrpLdrMacroInstructionHostedPatcher(AArch64MacroAssembler.AdrpLdrMacroInstruction macroInstruction) {
super(macroInstruction.instructionPosition);
this.macroInstruction = macroInstruction;
}
@Override
public void relocate(Reference ref, RelocatableBuffer relocs, int compStart) {
int siteOffset = compStart + macroInstruction.instructionPosition;
relocs.addRelocationWithAddend(siteOffset, RelocationKind.AARCH64_R_AARCH64_ADR_PREL_PG_HI21, Long.valueOf(0), ref);
siteOffset += 4;
RelocationKind secondRelocation;
switch (macroInstruction.srcSize) {
case 64:
secondRelocation = RelocationKind.AARCH64_R_AARCH64_LDST64_ABS_LO12_NC;
break;
case 32:
secondRelocation = RelocationKind.AARCH64_R_AARCH64_LDST32_ABS_LO12_NC;
break;
case 16:
secondRelocation = RelocationKind.AARCH64_R_AARCH64_LDST16_ABS_LO12_NC;
break;
case 8:
secondRelocation = RelocationKind.AARCH64_R_AARCH64_LDST8_ABS_LO12_NC;
break;
default:
throw VMError.shouldNotReachHere("Unknown macro instruction src size of " + macroInstruction.srcSize);
}
relocs.addRelocationWithAddend(siteOffset, secondRelocation, Long.valueOf(0), ref);
}
@Uninterruptible(reason = ".")
@Override
public void patch(int codePos, int relative, byte[] code) {
macroInstruction.patch(codePos, relative, code);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
}
class AdrpAddMacroInstructionHostedPatcher extends CompilationResult.CodeAnnotation implements HostedPatcher {
private final AArch64MacroAssembler.AdrpAddMacroInstruction macroInstruction;
AdrpAddMacroInstructionHostedPatcher(AArch64MacroAssembler.AdrpAddMacroInstruction macroInstruction) {
super(macroInstruction.instructionPosition);
this.macroInstruction = macroInstruction;
}
@Override
public void relocate(Reference ref, RelocatableBuffer relocs, int compStart) {
int siteOffset = compStart + macroInstruction.instructionPosition;
relocs.addRelocationWithAddend(siteOffset, RelocationKind.AARCH64_R_AARCH64_ADR_PREL_PG_HI21, Long.valueOf(0), ref);
siteOffset += 4;
relocs.addRelocationWithAddend(siteOffset, RelocationKind.AARCH64_R_AARCH64_ADD_ABS_LO12_NC, Long.valueOf(0), ref);
}
@Uninterruptible(reason = ".")
@Override
public void patch(int codePos, int relative, byte[] code) {
macroInstruction.patch(codePos, relative, code);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
}
class MovSequenceHostedPatcher extends CompilationResult.CodeAnnotation implements HostedPatcher {
private final AArch64MacroAssembler.MovSequenceAnnotation annotation;
MovSequenceHostedPatcher(AArch64MacroAssembler.MovSequenceAnnotation annotation) {
super(annotation.instructionPosition);
this.annotation = annotation;
}
@Override
public void relocate(Reference ref, RelocatableBuffer relocs, int compStart) {
int siteOffset = compStart + annotation.instructionPosition;
if (ref instanceof DataSectionReference || ref instanceof CGlobalDataReference || ref instanceof ConstantReference) {
int lastMovIndex = -1;
MovAction[] includeSet = annotation.includeSet;
for (int i = 0; i < includeSet.length; i++) {
switch (includeSet[i]) {
case USED:
lastMovIndex = i;
break;
case SKIPPED:
break;
case NEGATED:
throw VMError.shouldNotReachHere("Negated mov action isn't handled by relocation currently.");
}
}
RelocationKind[] relocations = {RelocationKind.AARCH64_R_MOVW_UABS_G0, RelocationKind.AARCH64_R_MOVW_UABS_G1, RelocationKind.AARCH64_R_MOVW_UABS_G2, RelocationKind.AARCH64_R_MOVW_UABS_G3};
RelocationKind[] noCheckRelocations = {RelocationKind.AARCH64_R_MOVW_UABS_G0_NC, RelocationKind.AARCH64_R_MOVW_UABS_G1_NC, RelocationKind.AARCH64_R_MOVW_UABS_G2_NC};
for (int i = 0; i < includeSet.length; i++) {
if (includeSet[i] == MovAction.SKIPPED) {
continue;
}
if (i == lastMovIndex) {
relocs.addRelocationWithAddend(siteOffset, relocations[i], Long.valueOf(0), ref);
} else {
relocs.addRelocationWithAddend(siteOffset, noCheckRelocations[i], Long.valueOf(0), ref);
}
siteOffset = siteOffset + 4;
}
} else {
throw VMError.shouldNotReachHere("Unknown type of reference in code");
}
}
@Uninterruptible(reason = ".")
@Override
public void patch(int codePos, int relative, byte[] code) {
annotation.patch(codePos, relative, code);
}
@Override
public boolean equals(Object obj) {
return obj == this;
}
}