All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.qbicc.plugin.llvm.LLVMStackMapCollector Maven / Gradle / Ivy

package org.qbicc.plugin.llvm;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.qbicc.context.CompilationContext;
import org.qbicc.context.Location;
import org.qbicc.context.ProgramLocatable;
import org.qbicc.driver.Driver;
import org.qbicc.graph.InvocationNode;
import org.qbicc.graph.Node;
import org.qbicc.machine.llvm.CallingConvention;
import org.qbicc.machine.llvm.stackmap.LocationType;
import org.qbicc.machine.llvm.stackmap.StackMap;
import org.qbicc.machine.llvm.stackmap.StackMapVisitor;
import org.qbicc.machine.object.ObjectFile;
import org.qbicc.machine.object.ObjectFileProvider;
import org.qbicc.object.Function;
import org.qbicc.plugin.linker.Linker;
import org.qbicc.plugin.methodinfo.CallSiteTable;
import org.qbicc.plugin.methodinfo.valueinfo.FrameOffsetValueInfo;
import org.qbicc.plugin.methodinfo.valueinfo.RegisterRelativeValueInfo;
import org.qbicc.plugin.methodinfo.valueinfo.RegisterValueInfo;
import org.qbicc.plugin.methodinfo.valueinfo.ValueInfo;
import org.qbicc.type.definition.LoadedTypeDefinition;

/**
 * A utility to collect stack map data from an object file produced from LLVM which includes statepoint calls.
 * 

* Note that statepoint uses the stack map format slightly differently than described in the stack map documentation. * Specifically, each stack map starts with three constant entries which give information about the usage site, * and there are two consecutive entries for each actual variable (one corresponding to the base pointer and one for * the derived pointer, which in our case is presently always the same as the base pointer). */ public final class LLVMStackMapCollector { private final CompilationContext ctxt; public LLVMStackMapCollector(CompilationContext ctxt) { this.ctxt = ctxt; } public void collect() { Linker linker = Linker.get(ctxt); LLVMInfo info = LLVMInfo.get(ctxt); ObjectFileProvider objFileProvider = ctxt.getAttachment(Driver.OBJ_PROVIDER_TOOL_KEY); Iterator> objFileIterator = linker.getObjectFilePathsWithTypeInLinkOrder().iterator(); final CallSiteTable cst = CallSiteTable.get(ctxt); ctxt.runParallelTask(ctxt -> { Map.Entry entry; for (; ; ) { synchronized (objFileIterator) { if (!objFileIterator.hasNext()) { return; } entry = objFileIterator.next(); } final LoadedTypeDefinition typeDefinition = entry.getKey(); final List callSitesById = info.getStatePointIds(typeDefinition); if (callSitesById == null) { throw new IllegalStateException("Missing statepoint IDs"); } final Path objFile = entry.getValue(); try (ObjectFile objectFile = objFileProvider.openObjectFile(objFile)) { org.qbicc.machine.object.Section stackMapSection = objectFile.getSection(objectFile.getStackMapSectionName()); if (stackMapSection != null) { ByteBuffer stackMapData = stackMapSection.getSectionContent(); StackMap.parse(stackMapData, new StackMapVisitor() { // per-unit private final List callSites = new ArrayList<>(); // per-function private long fnIndex; private long stackSize; private final List fnCallSites = new ArrayList<>(); // per-call-site private CallingConvention cconv; private long patchPointId; private long offset; private final HashSet valueInfos = new HashSet<>(); public void start(int version, long fnCount, long recCount) { if (version != 3) { ctxt.error(Location.builder().setSourceFilePath(objectFile.toString()).build(), "Stack map version %d not supported", Integer.valueOf(version)); } } public void startFunction(long fnIndex, long address, long stackSize, long recordCount) { this.fnIndex = fnIndex; this.stackSize = stackSize; } public void startRecord(long recIndex, long patchPointId, long offset, int locCnt, int liveOutCnt) { this.offset = offset; this.patchPointId = patchPointId; } private CallSiteTable.SourceCodeEntry getSourceCodeEntry(ProgramLocatable node) { final ProgramLocatable inlinedAt = node.callSite(); return cst.intern(new CallSiteTable.SourceCodeEntry(cst.getSubprogramEntry(node.element()), node.lineNumber(), node.bytecodeIndex(), inlinedAt == null ? null : getSourceCodeEntry(inlinedAt))); } public void location(int locIndex, LocationType type, int size, int regNum, long data) { switch (locIndex) { case 0 -> { // calling convention switch (type) { case Constant -> cconv = CallingConvention.values()[(int)data]; default -> ctxt.error(Location.builder().setSourceFilePath(objectFile.toString()).build(), "Unexpected entry for calling convention"); } } case 1 -> { // flags (ignore) switch (type) { case Constant -> { } default -> ctxt.error(Location.builder().setSourceFilePath(objectFile.toString()).build(), "Unexpected entry for flags"); } } case 2 -> { // deopt locations (ignore) switch (type) { case Constant -> { if (data != 0) { ctxt.error(Location.builder().setSourceFilePath(objectFile.toString()).build(), "Unexpected non-zero entry for deopt locations"); } } default -> ctxt.error(Location.builder().setSourceFilePath(objectFile.toString()).build(), "Unexpected entry for deopt locations"); } } default -> { // gather both base and derived pointers switch (type) { case Register -> valueInfos.add(RegisterValueInfo.forRegisterNumber(regNum)); case Direct -> valueInfos.add(new RegisterRelativeValueInfo(RegisterValueInfo.forRegisterNumber(regNum), (int) (data / ctxt.getTypeSystem().getReferenceSize()))); case Indirect -> valueInfos.add(new FrameOffsetValueInfo(RegisterValueInfo.forRegisterNumber(regNum), (int) (data / ctxt.getTypeSystem().getReferenceSize()))); case Constant -> { if (data != 0) { ctxt.error(Location.builder().setSourceFilePath(objectFile.toString()).build(), "Constant stack map record not supported"); } // otherwise ignore (null values are expected) } } } } } public void endRecord(long recIndex) { if (valueInfos.isEmpty()) { // don't emit anything return; } final CallSiteTable.LiveValueInfo lvi = cst.intern(valueInfos); valueInfos.clear(); final Node node = callSitesById.get(Math.toIntExact(patchPointId)); // todo: Replace the `address` argument with a Literal which represents the relocation with offset; // the address is actually a relocation... but we can cheat and just grab the function itself by index Function functionAddress = ctxt.getOrAddProgramModule(typeDefinition).getFunction((int) fnIndex); fnCallSites.add(new CallSiteTable.CallSiteEntry(functionAddress, offset, getSourceCodeEntry(node), lvi)); } public void endFunction(long fnIndex) { if (fnCallSites.isEmpty()) { // don't emit anything return; } fnCallSites.sort(Comparator.comparingLong(CallSiteTable.CallSiteEntry::offset)); callSites.addAll(fnCallSites); fnCallSites.clear(); } public void end() { if (callSites.isEmpty()) { // don't emit anything return; } cst.registerEntries(typeDefinition, callSites); callSites.clear(); } }); } } catch (IOException e) { ctxt.error(Location.builder().setSourceFilePath(String.valueOf(objFile)).build(), "Failed to read stack map information: %s", e); } } }); } public static void execute(final CompilationContext ctxt) { new LLVMStackMapCollector(ctxt).collect(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy