package com.oracle.svm.hosted.image;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.graalvm.compiler.code.CompilationResult;
import org.graalvm.compiler.code.CompilationResult.CodeAnnotation;
import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.Indent;
import org.graalvm.compiler.serviceprovider.BufferUtil;
import org.graalvm.word.WordFactory;
import com.oracle.graal.pointsto.BigBang;
import com.oracle.objectfile.ObjectFile;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.meta.SubstrateObjectConstant;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.code.HostedImageHeapConstantPatch;
import com.oracle.svm.hosted.code.HostedPatcher;
import com.oracle.svm.hosted.image.NativeBootImage.NativeTextSectionImpl;
import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.meta.MethodPointer;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.code.site.Call;
import jdk.vm.ci.code.site.DataPatch;
import jdk.vm.ci.code.site.Infopoint;
import jdk.vm.ci.code.site.Reference;
public class LIRNativeImageCodeCache extends NativeImageCodeCache {
private static final byte CODE_FILLER_BYTE = (byte) 0xCC;
private int codeCacheSize;
private final TargetDescription target;
public LIRNativeImageCodeCache(Map<HostedMethod, CompilationResult> compilations, NativeImageHeap imageHeap) {
super(compilations, imageHeap);
target = ConfigurationValues.getTarget();
}
@Override
public int getCodeCacheSize() {
assert codeCacheSize > 0;
return codeCacheSize;
}
@SuppressWarnings("try")
@Override
public void layoutMethods(DebugContext debug, String imageName, BigBang bb, ForkJoinPool threadPool) {
try (Indent indent = debug.logAndIndent("layout methods")) {
assert codeCacheSize == 0;
HostedMethod firstMethod = null;
for (Entry<HostedMethod, CompilationResult> entry : compilations.entrySet()) {
HostedMethod method = entry.getKey();
if (firstMethod == null) {
firstMethod = method;
}
CompilationResult compilation = entry.getValue();
compilationsByStart.put(codeCacheSize, compilation);
method.setCodeAddressOffset(codeCacheSize);
codeCacheSize = NumUtil.roundUp(codeCacheSize + compilation.getTargetCodeSize(), SubstrateOptions.codeAlignment());
}
buildRuntimeMetadata(MethodPointer.factory(firstMethod), WordFactory.unsigned(codeCacheSize));
}
}
@Override
@SuppressWarnings("try")
public void patchMethods(DebugContext debug, RelocatableBuffer relocs, ObjectFile objectFile) {
for (Entry<HostedMethod, CompilationResult> entry : compilations.entrySet()) {
HostedMethod method = entry.getKey();
CompilationResult compilation = entry.getValue();
int compStart = method.getCodeAddressOffset();
Map<Integer, HostedPatcher> patches = new HashMap<>();
ByteBuffer targetCode = null;
for (CodeAnnotation codeAnnotation : compilation.getCodeAnnotations()) {
if (codeAnnotation instanceof HostedPatcher) {
patches.put(codeAnnotation.getPosition(), (HostedPatcher) codeAnnotation);
} else if (codeAnnotation instanceof HostedImageHeapConstantPatch) {
HostedImageHeapConstantPatch patch = (HostedImageHeapConstantPatch) codeAnnotation;
ObjectInfo objectInfo = imageHeap.getObjectInfo(SubstrateObjectConstant.asObject(patch.constant));
long objectAddress = objectInfo.getAddress();
if (targetCode == null) {
targetCode = ByteBuffer.wrap(compilation.getTargetCode()).order(target.arch.getByteOrder());
}
int originalValue = targetCode.getInt(patch.getPosition());
long newValue = originalValue + objectAddress;
VMError.guarantee(NumUtil.isInt(newValue), "Image heap size is limited to 2 GByte");
targetCode.putInt(patch.getPosition(), (int) newValue);
}
}
for (Infopoint infopoint : compilation.getInfopoints()) {
if (infopoint instanceof Call && ((Call) infopoint).direct) {
Call call = (Call) infopoint;
int callTargetStart = ((HostedMethod) call.target).getCodeAddressOffset();
int pcDisplacement = callTargetStart - (compStart + call.pcOffset);
patches.get(call.pcOffset).patch(call.pcOffset, pcDisplacement, compilation.getTargetCode());
}
}
for (DataPatch dataPatch : compilation.getDataPatches()) {
Reference ref = dataPatch.reference;
patches.get(dataPatch.pcOffset).relocate(ref, relocs, compStart);
}
try (DebugContext.Scope ds = debug.scope("After Patching", method.asJavaMethod())) {
debug.dump(DebugContext.BASIC_LEVEL, compilation, "After patching");
} catch (Throwable e) {
throw VMError.shouldNotReachHere(e);
}
}
}
@Override
public void writeCode(RelocatableBuffer buffer) {
ByteBuffer bufferBytes = buffer.getByteBuffer();
int startPos = bufferBytes.position();
for (Entry<HostedMethod, CompilationResult> entry : compilations.entrySet()) {
HostedMethod method = entry.getKey();
CompilationResult compilation = entry.getValue();
BufferUtil.asBaseBuffer(bufferBytes).position(startPos + method.getCodeAddressOffset());
int codeSize = compilation.getTargetCodeSize();
bufferBytes.put(compilation.getTargetCode(), 0, codeSize);
for (int i = codeSize; i < NumUtil.roundUp(codeSize, SubstrateOptions.codeAlignment()); i++) {
bufferBytes.put(CODE_FILLER_BYTE);
}
}
BufferUtil.asBaseBuffer(bufferBytes).position(startPos);
}
@Override
public NativeTextSectionImpl getTextSectionImpl(RelocatableBuffer buffer, ObjectFile objectFile, NativeImageCodeCache codeCache) {
return new NativeTextSectionImpl(buffer, objectFile, codeCache) {
@Override
protected void defineMethodSymbol(String name, boolean global, ObjectFile.Element section, HostedMethod method, CompilationResult result) {
final int size = result == null ? 0 : result.getTargetCodeSize();
objectFile.createDefinedSymbol(name, section, method.getCodeAddressOffset(), size, true, global);
}
};
}
@Override
public List<ObjectFile.Symbol> getSymbols(ObjectFile objectFile, boolean onlyGlobal) {
Stream<ObjectFile.Symbol> stream = StreamSupport.stream(objectFile.getSymbolTable().spliterator(), false);
if (onlyGlobal) {
stream = stream.filter(ObjectFile.Symbol::isGlobal);
}
return stream.filter(ObjectFile.Symbol::isDefined).collect(Collectors.toList());
}
}