/*
* Copyright (c) 2016, Oracle and/or its affiliates.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.truffle.llvm.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.llvm.parser.model.blocks.InstructionBlock;
import com.oracle.truffle.llvm.parser.model.functions.FunctionDeclaration;
import com.oracle.truffle.llvm.parser.model.functions.FunctionDefinition;
import com.oracle.truffle.llvm.parser.model.globals.GlobalValueSymbol;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.AllocateInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.BinaryOperationInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.BranchInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.CallInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.CastInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.CompareExchangeInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.CompareInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.ConditionalBranchInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.ExtractElementInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.ExtractValueInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.GetElementPointerInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.IndirectBranchInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.InsertElementInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.InsertValueInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.Instruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.InvokeInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.LandingpadInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.LoadInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.PhiInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.ResumeInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.ReturnInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.SelectInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.ShuffleVectorInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.StoreInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.SwitchInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.SwitchOldInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.TerminatingInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.UnreachableInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.ValueInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.VoidCallInstruction;
import com.oracle.truffle.llvm.parser.model.symbols.instructions.VoidInvokeInstruction;
import com.oracle.truffle.llvm.parser.model.visitors.AbstractTerminatingInstructionVisitor;
import com.oracle.truffle.llvm.parser.model.visitors.InstructionVisitor;
import com.oracle.truffle.llvm.parser.model.visitors.ValueInstructionVisitor;
import com.oracle.truffle.llvm.parser.util.LLVMParserAsserts;
import com.oracle.truffle.llvm.runtime.LLVMLogger;
import com.oracle.truffle.llvm.runtime.options.LLVMOptions;
import com.oracle.truffle.llvm.runtime.types.symbols.Symbol;
import com.oracle.truffle.llvm.runtime.types.symbols.ValueSymbol;
public final class LLVMLifetimeAnalysis {
private final Map<InstructionBlock, FrameSlot[]> nullableBefore;
private final Map<InstructionBlock, FrameSlot[]> nullableAfter;
public LLVMLifetimeAnalysis(Map<InstructionBlock, FrameSlot[]> nullableBefore, Map<InstructionBlock, FrameSlot[]> nullableAfter) {
this.nullableBefore = nullableBefore;
this.nullableAfter = nullableAfter;
}
public Map<InstructionBlock, FrameSlot[]> getNullableBefore() {
return nullableBefore;
}
public Map<InstructionBlock, FrameSlot[]> getNullableAfter() {
return nullableAfter;
}
public static LLVMLifetimeAnalysis getResult(FunctionDefinition functionDefinition, FrameDescriptor frameDescriptor, Map<InstructionBlock, List<LLVMPhiManager.Phi>> phiRefs) {
LLVMParserAsserts.assertNoNullElement(frameDescriptor.getSlots());
final LLVMLifetimeAnalysisVisitor visitor = new LLVMLifetimeAnalysisVisitor(frameDescriptor, functionDefinition, phiRefs);
final LLVMLifetimeAnalysis lifetimes = visitor.visit();
if (!LLVMLogger.TARGET_NONE.equals(LLVMOptions.DEBUG.printLifetimeAnalysisStatistics())) {
printResult(functionDefinition, lifetimes);
}
return lifetimes;
}
private static final String FUNCTION_FORMAT = "%s:\n";
private static final String AFTER_BLOCK_FORMAT = "\t dead after bb %4s:";
private static void printResult(FunctionDefinition functionDefinition, LLVMLifetimeAnalysis lifetimes) {
final Map<InstructionBlock, FrameSlot[]> mapping = lifetimes.getNullableAfter();
final StringBuilder builder = new StringBuilder();
builder.append(String.format(FUNCTION_FORMAT, functionDefinition.getName()));
builder.append('\n');
for (InstructionBlock b : mapping.keySet()) {
builder.append(String.format(AFTER_BLOCK_FORMAT, b.getName())).append('\n');
FrameSlot[] variables = mapping.get(b);
if (variables.length != 0) {
builder.append("\t");
for (int i = 0; i < variables.length; i++) {
if (i != 0) {
builder.append(", ");
}
builder.append(variables[i].getIdentifier());
}
}
builder.append('\n');
}
LLVMLogger.print(LLVMOptions.DEBUG.printLifetimeAnalysisStatistics()).accept(builder.toString());
}
private static final class LLVMReadVisitor {
static List<FrameSlot> getReads(Instruction instruction, FrameDescriptor frame, boolean alsoCountPhiUsage) {
final List<FrameSlot> reads = new ArrayList<>();
instruction.accept(new InstructionVisitor() {
private void resolve(Symbol symbol) {
if (symbol.hasName() && !(symbol instanceof GlobalValueSymbol || symbol instanceof FunctionDefinition || symbol instanceof FunctionDeclaration)) {
String name = ((ValueSymbol) symbol).getName();
assert name != null;
final FrameSlot frameSlot = frame.findFrameSlot(name);
if (frameSlot == null) {
throw new AssertionError("No Frameslot for ValueSymbol: " + symbol);
} else {
reads.add(frameSlot);
}
}
}
@Override
public void visit(AllocateInstruction allocate) {
resolve(allocate.getCount());
}
@Override
public void visit(BinaryOperationInstruction operation) {
resolve(operation.getLHS());
resolve(operation.getRHS());
}
@Override
public void visit(BranchInstruction branch) {
}
@Override
public void visit(InvokeInstruction call) {
for (int i = 0; i < call.getArgumentCount(); i++) {
resolve(call.getArgument(i));
}
resolve(call.getCallTarget());
}
@Override
public void visit(CallInstruction call) {
for (int i = 0; i < call.getArgumentCount(); i++) {
resolve(call.getArgument(i));
}
resolve(call.getCallTarget());
}
@Override
public void visit(CastInstruction cast) {
resolve(cast.getValue());
}
@Override
public void visit(LandingpadInstruction landingpadInstruction) {
if (landingpadInstruction.getValue() != null) {
resolve(landingpadInstruction.getValue());
}
}
@Override
public void visit(CompareInstruction operation) {
resolve(operation.getLHS());
resolve(operation.getRHS());
}
@Override
public void visit(ConditionalBranchInstruction branch) {
resolve(branch.getCondition());
}
@Override
public void visit(ExtractElementInstruction extract) {
resolve(extract.getIndex());
resolve(extract.getVector());
}
@Override
public void visit(ExtractValueInstruction extract) {
resolve(extract.getAggregate());
}
@Override
public void visit(GetElementPointerInstruction gep) {
resolve(gep.getBasePointer());
gep.getIndices().forEach(this::resolve);
}
@Override
public void visit(IndirectBranchInstruction branch) {
resolve(branch.getAddress());
}
@Override
public void visit(InsertElementInstruction insert) {
resolve(insert.getVector());
resolve(insert.getIndex());
resolve(insert.getValue());
}
@Override
public void visit(InsertValueInstruction insert) {
resolve(insert.getAggregate());
resolve(insert.getValue());
}
@Override
public void visit(LoadInstruction load) {
resolve(load.getSource());
}
@Override
public void visit(PhiInstruction phi) {
if (alsoCountPhiUsage) {
for (int i = 0; i < phi.getSize(); i++) {
resolve(phi.getValue(i));
}
}
}
@Override
public void visit(ReturnInstruction ret) {
if (ret.getValue() != null) {
resolve(ret.getValue());
}
}
@Override
public void visit(ResumeInstruction resume) {
if (resume.getValue() != null) {
resolve(resume.getValue());
}
}
@Override
public void visit(CompareExchangeInstruction cmpxchg) {
resolve(cmpxchg.getPtr());
resolve(cmpxchg.getCmp());
resolve(cmpxchg.getReplace());
}
@Override
public void visit(SelectInstruction select) {
resolve(select.getCondition());
resolve(select.getTrueValue());
resolve(select.getFalseValue());
}
@Override
public void visit(ShuffleVectorInstruction shuffle) {
resolve(shuffle.getMask());
resolve(shuffle.getVector1());
resolve(shuffle.getVector2());
}
@Override
public void visit(StoreInstruction store) {
resolve(store.getDestination());
resolve(store.getSource());
}
@Override
public void visit(SwitchInstruction select) {
// everything but the condition must be an integer constant anyways, they do not
// lie on the stack
resolve(select.getCondition());
}
@Override
public void visit(SwitchOldInstruction select) {
resolve(select.getCondition());
}
@Override
public void visit(UnreachableInstruction unreachable) {
}
@Override
public void visit(VoidCallInstruction call) {
for (int i = 0; i < call.getArgumentCount(); i++) {
resolve(call.getArgument(i));
}
resolve(call.getCallTarget());
}
@Override
public void visit(VoidInvokeInstruction call) {
for (int i = 0; i < call.getArgumentCount(); i++) {
resolve(call.getArgument(i));
}
resolve(call.getCallTarget());
}
});
LLVMParserAsserts.assertNoNullElement(reads);
return reads;
}
}
private static final class LLVMLifetimeAnalysisVisitor {
private final List<InstructionBlock> basicBlocks;
private final FrameDescriptor frame;
private final FunctionDefinition functionDefinition;
private final Map<InstructionBlock, List<LLVMPhiManager.Phi>> phiRefs;
private final Map<Instruction, List<FrameSlot>> instructionReads = new HashMap<>();
private final Map<Instruction, List<InstructionBlock>> successorBlocks = new HashMap<>();
private final Map<Instruction, Set<FrameSlot>> bbEndKills = new HashMap<>();
private final Map<InstructionBlock, Set<FrameSlot>> bbBeginKills = new HashMap<>();
/**
* The variable definitions per instruction (the last instruction can have several.
*/
private final Map<Instruction, Set<FrameSlot>> defs = new HashMap<>();
/**
* The (transitive) inputs of each instruction.
*/
private final Map<Instruction, Set<FrameSlot>> in = new HashMap<>();
/**
* The (transitive) outputs of each instruction.
*/
private final Map<Instruction, Set<FrameSlot>> out = new HashMap<>();
LLVMLifetimeAnalysisVisitor(FrameDescriptor frame, FunctionDefinition functionDefinition, Map<InstructionBlock, List<LLVMPhiManager.Phi>> phiRefs) {
this.frame = frame;
this.functionDefinition = functionDefinition;
this.phiRefs = phiRefs;
this.basicBlocks = new ArrayList<>(functionDefinition.getBlocks());
}
private FrameSlot getFrameSlot(String name) {
final FrameSlot frameSlot = frame.findFrameSlot(name);
if (frameSlot == null) {
throw new AssertionError("No FrameSlot with name: " + name);
} else {
return frameSlot;
}
}
private void initializeSuccessors() {
final InstructionVisitor initSuccessorsVisitor = new AbstractTerminatingInstructionVisitor() {
@Override
public void visitTerminatingInstruction(TerminatingInstruction instruction) {
successorBlocks.put((Instruction) instruction, instruction.getSuccessors());
}
};
functionDefinition.accept(block -> block.accept(initSuccessorsVisitor));
}
private void initializeInstructionReads() {
functionDefinition.accept(block -> {
for (int i = 0; i < block.getInstructionCount(); i++) {
final Instruction inst = block.getInstruction(i);
final List<FrameSlot> currentInstructionReads = LLVMReadVisitor.getReads(inst, frame, false);
LLVMParserAsserts.assertNoNullElement(currentInstructionReads);
instructionReads.put(inst, currentInstructionReads);
}
});
}
private void initializeInstructionInOuts() {
final InstructionVisitor initEndKillsVisitor = new ValueInstructionVisitor() {
@Override
public void visitValueInstruction(ValueInstruction valueInstruction) {
bbEndKills.put(valueInstruction, new HashSet<>());
}
};
functionDefinition.accept(bb -> {
bbBeginKills.put(bb, new HashSet<>());
final List<LLVMPhiManager.Phi> bbPhis = phiRefs.getOrDefault(bb, Collections.emptyList());
bb.accept(initEndKillsVisitor);
for (int i = 0; i < bb.getInstructionCount(); i++) {
final Instruction inst = bb.getInstruction(i);
// in[n] = use[n]
// variables inside phi instructions do not have usages since they are actually
// written before
final Set<FrameSlot> uses = new HashSet<>(instructionReads.getOrDefault(inst, Collections.emptyList()));
// so we have to add the usage of the phi instructions where they are written
// (at the last instruction of a block)
if (i == bb.getInstructionCount() - 1) {
for (final LLVMPhiManager.Phi phi : bbPhis) {
final Symbol val = phi.getValue();
if (val.hasName() && !(val instanceof GlobalValueSymbol || val instanceof FunctionDefinition || val instanceof FunctionDeclaration)) {
String name = ((ValueSymbol) val).getName();
assert name != null;
uses.add(getFrameSlot(name));
}
}
}
in.put(inst, uses);
out.put(inst, new HashSet<>());
}
});
}
private void initializeVariableDefinitions() {
final InstructionVisitor initVarDefVisitor = new ValueInstructionVisitor() {
@Override
public void visitValueInstruction(ValueInstruction valueInstruction) {
final Set<FrameSlot> instructionDefs = new HashSet<>(1);
instructionDefs.add(getFrameSlot(valueInstruction.getName()));
defs.put(valueInstruction, instructionDefs);
}
};
functionDefinition.accept(block -> block.accept(initVarDefVisitor));
}
private void findFixPoint() {
boolean changed;
final List<InstructionBlock> reversedBlocks = new ArrayList<>(basicBlocks);
Collections.reverse(reversedBlocks);
do {
changed = false;
for (final InstructionBlock block : reversedBlocks) {
for (int i = 0; i < block.getInstructionCount(); i++) {
final Instruction inst = block.getInstruction(i);
// update out
if (inst instanceof TerminatingInstruction) {
// non sequential successor
// out[n] = in[n+1, n+2, ...]
assert i == block.getInstructionCount() - 1;
final List<InstructionBlock> nextBlocks = successorBlocks.getOrDefault(inst, Collections.emptyList());
for (InstructionBlock nextBlock : nextBlocks) {
final Instruction nextInst = getFirstNonPhiInstruction(nextBlock);
final Set<FrameSlot> nextIn = in.getOrDefault(nextInst, new HashSet<>(0));
final Set<FrameSlot> addTo = out.getOrDefault(inst, new HashSet<>(0));
changed |= addFrameSlots(addTo, nextIn);
}
} else {
// out[n] = in[n + 1]
assert i + 1 < block.getInstructionCount();
final Instruction nextInst = block.getInstruction(i + 1);
final Set<FrameSlot> nextIn = in.getOrDefault(nextInst, new HashSet<>(0));
final Set<FrameSlot> addTo = out.getOrDefault(inst, new HashSet<>(0));
changed |= addFrameSlots(addTo, nextIn);
}
// update in
final Set<FrameSlot> outWithoutDefs = new HashSet<>(out.getOrDefault(inst, new HashSet<>(0)));
final List<FrameSlot> realDefs = new ArrayList<>(defs.getOrDefault(inst, new HashSet<>(0)));
outWithoutDefs.removeAll(realDefs);
changed |= addFrameSlots(in.getOrDefault(inst, new HashSet<>(0)), outWithoutDefs);
}
}
} while (changed);
}
private static Instruction getFirstNonPhiInstruction(InstructionBlock block) {
// Phi-Instruction only appear at the start of basic blocks, but there can be
// arbitrarily many
for (int i = 0; i < block.getInstructionCount(); i++) {
final Instruction inst = block.getInstruction(i);
if (!(inst instanceof PhiInstruction)) {
return inst;
}
}
throw new AssertionError("Block without ending Instruction!");
}
private void getInstructionKills(Map<Instruction, Set<FrameSlot>> kills) {
for (final InstructionBlock bb : basicBlocks) {
for (int i = 0; i < bb.getInstructionCount(); i++) {
final Instruction inst = bb.getInstruction(i);
final Set<FrameSlot> inSlots = in.getOrDefault(inst, new HashSet<>(0));
final Set<FrameSlot> outSlots = out.getOrDefault(inst, new HashSet<>(0));
final Set<FrameSlot> instructionKills = new HashSet<>(inSlots);
instructionKills.removeAll(outSlots);
if (inst instanceof TerminatingInstruction) {
for (final InstructionBlock bas : successorBlocks.getOrDefault(inst, Collections.emptyList())) {
final Instruction firstInst = getFirstNonPhiInstruction(bas);
Set<FrameSlot> deadAtBegin = new HashSet<>(out.getOrDefault(inst, new HashSet<>(0)));
deadAtBegin.removeAll(in.getOrDefault(firstInst, new HashSet<>(0)));
bbBeginKills.put(bas, deadAtBegin);
}
if (inst instanceof ReturnInstruction || inst instanceof UnreachableInstruction) {
kills.put(inst, new HashSet<>(frame.getSlots()));
}
}
kills.put(inst, instructionKills);
}
}
}
private Map<InstructionBlock, FrameSlot[]> convertInstructionKillsToBasicBlockKills() {
final Map<InstructionBlock, FrameSlot[]> convertedMap = new HashMap<>();
for (final InstructionBlock bb : basicBlocks) {
final List<FrameSlot> blockKills = new ArrayList<>();
for (int i = 0; i < bb.getInstructionCount(); i++) {
blockKills.addAll(bbEndKills.getOrDefault(bb.getInstruction(i), new HashSet<>(0)));
}
final FrameSlot[] blockKillArr = blockKills.toArray(new FrameSlot[blockKills.size()]);
convertedMap.put(bb, blockKillArr);
}
return convertedMap;
}
private LLVMLifetimeAnalysis visit() {
initializeSuccessors();
initializeInstructionReads();
initializeInstructionInOuts();
initializeVariableDefinitions();
findFixPoint();
getInstructionKills(bbEndKills);
final Map<InstructionBlock, FrameSlot[]> endKills = convertInstructionKillsToBasicBlockKills();
final Map<InstructionBlock, FrameSlot[]> beginKills = new HashMap<>();
for (InstructionBlock block : basicBlocks) {
final Set<FrameSlot> bbBegin = bbBeginKills.getOrDefault(block, new HashSet<>(0));
beginKills.put(block, bbBegin.toArray(new FrameSlot[bbBegin.size()]));
}
return new LLVMLifetimeAnalysis(beginKills, endKills);
}
private static boolean addFrameSlots(Set<FrameSlot> addTo, Set<FrameSlot> add) {
return addTo.addAll(add);
}
}
}