/*
* Copyright (c) 2013-2016 Chris Newland.
* Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
* Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
*/
package org.adoptopenjdk.jitwatch.model.assembly;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COLON;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_ASSEMBLY;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HEX_PREFIX;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_COMMA;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOUBLE_SPACE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPTIMIZED_VIRTUAL_CALL;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SAFEPOINT_POLL;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SAFEPOINT_POLL_RETURN;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.adoptopenjdk.jitwatch.optimizedvcall.VirtualCallSite;
import org.adoptopenjdk.jitwatch.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AssemblyInstruction
{
private String annotation;
private long address;
private List<String> prefixes;
private String mnemonic;
private List<String> operands = new ArrayList<>();
private List<String> commentLines = new ArrayList<>();
private final AssemblyLabels labels;
private boolean isSafePoint = false;
private static final Pattern PATTERN_ASSEMBLY_CALL_SIG = Pattern.compile("^; - (.*)::(.*)@(.*)\\s\\(line\\s(.*)\\)");
private static final Logger logger = LoggerFactory.getLogger(AssemblyInstruction.class);
public AssemblyInstruction(String annotation, long address, List<String> prefixes, String mnemonic, List<String> operands,
String firstComment, AssemblyLabels labels)
{
this.annotation = annotation;
this.address = address;
this.prefixes = prefixes;
this.mnemonic = mnemonic;
this.operands = operands;
this.labels = labels;
if (firstComment != null)
{
this.commentLines.add(firstComment.trim());
}
}
public String getAnnotation()
{
return annotation;
}
public long getAddress()
{
return address;
}
public List<String> getPrefixes()
{
return prefixes;
}
public String getMnemonic()
{
return mnemonic;
}
public List<String> getOperands()
{
return operands;
}
public String getComment()
{
StringBuilder builder = new StringBuilder();
if (commentLines.size() > 0)
{
for (String line : commentLines)
{
builder.append(line).append(S_NEWLINE);
}
builder.deleteCharAt(builder.length() - 1);
}
return builder.toString();
}
public List<String> getCommentLines()
{
return commentLines;
}
public void addCommentLine(String comment)
{
if (comment != null)
{
commentLines.add(comment);
if (!isSafePoint)
{
isSafePoint = comment.contains(S_SAFEPOINT_POLL) || comment.contains(S_SAFEPOINT_POLL_RETURN);
}
}
}
public boolean isSafePoint()
{
return isSafePoint;
}
public void appendToLastCommentLine(String comment)
{
if (comment != null)
{
String lastCommentLine = commentLines.get(commentLines.size() - 1);
commentLines.set(commentLines.size() - 1, lastCommentLine + comment);
}
}
public boolean isOptimizedVCall()
{
boolean result = false;
int commentLineCount = commentLines.size();
if (commentLineCount > 1)
{
String lastLine = commentLines.get(commentLineCount - 1);
if (lastLine.contains(S_OPTIMIZED_VIRTUAL_CALL))
{
result = true;
}
}
return result;
}
public VirtualCallSite getOptimizedVirtualCallSiteOrNull()
{
VirtualCallSite result = null;
if (isOptimizedVCall())
{
// Oop comment
// *invoke comment
// callsite comment+
// optimized virtual_call
String callSiteCommentLine = commentLines.get(2);
Matcher matcher = PATTERN_ASSEMBLY_CALL_SIG.matcher(callSiteCommentLine);
if (matcher.find())
{
String className = matcher.group(1);
String methodName = matcher.group(2);
String bytecodeOffset = matcher.group(3);
String lineNumber = matcher.group(4);
try
{
result = new VirtualCallSite(className, methodName, Integer.parseInt(bytecodeOffset),
Integer.parseInt(lineNumber));
}
catch (NumberFormatException nfe)
{
if (DEBUG_LOGGING_ASSEMBLY)
{
logger.warn("Could not parse CallSite from line: {}", callSiteCommentLine);
}
}
}
}
return result;
}
@Override
public String toString()
{
return toString(0, false);
}
public String toString(int annoWidth, boolean useLocalLabels)
{
StringBuilder builder = new StringBuilder();
builder.append(StringUtil.alignLeft(annotation, annoWidth));
if (useLocalLabels)
{
labels.formatAddress(address, builder);
}
else
{
builder.append(S_HEX_PREFIX).append(StringUtil.pad(Long.toHexString(address), 16, '0', true));
}
builder.append(C_COLON).append(C_SPACE);
if (!prefixes.isEmpty())
{
for (String prefix : prefixes)
{
builder.append(prefix);
builder.append(C_SPACE);
}
}
builder.append(mnemonic);
if (useLocalLabels)
{
labels.formatOperands(this, builder);
}
else
{
if (operands.size() > 0)
{
builder.append(C_SPACE);
for (String op : operands)
{
builder.append(op).append(S_COMMA);
}
builder.deleteCharAt(builder.length() - 1);
}
}
int lineLength = builder.length();
if (commentLines.size() > 0)
{
boolean first = true;
for (String commentLine : commentLines)
{
if (first)
{
builder.append(S_DOUBLE_SPACE).append(commentLine).append(S_NEWLINE);
first = false;
}
else
{
builder.append(StringUtil.repeat(C_SPACE, lineLength + 2));
builder.append(commentLine).append(S_NEWLINE);
}
}
}
else
{
builder.append(S_NEWLINE);
}
return StringUtil.rtrim(builder.toString());
}
// Allow splitting an instruction with a multi-line comment across multiple
// labels which all contain the instruction
public String toString(int annoWidth, int line, boolean useLocalLabels)
{
StringBuilder builder = new StringBuilder();
builder.append(StringUtil.alignLeft(annotation, annoWidth));
if (useLocalLabels)
{
labels.formatAddress(address, builder);
}
else
{
builder.append(S_HEX_PREFIX).append(StringUtil.pad(Long.toHexString(address), 16, '0', true));
}
builder.append(C_COLON).append(C_SPACE);
if (!prefixes.isEmpty())
{
for (String prefix : prefixes)
{
builder.append(prefix);
builder.append(C_SPACE);
}
}
builder.append(mnemonic);
if (useLocalLabels)
{
labels.formatOperands(this, builder);
}
else
{
if (operands.size() > 0)
{
builder.append(C_SPACE);
for (String op : operands)
{
builder.append(op).append(S_COMMA);
}
builder.deleteCharAt(builder.length() - 1);
}
}
int lineLength = builder.length();
if (commentLines.size() > 0)
{
String comment = commentLines.get(line);
if (line == 0)
{
// first comment on same line as instruction
builder.append(S_DOUBLE_SPACE).append(comment);
}
else
{
// later comments on own line
builder.delete(0, builder.length());
builder.append(StringUtil.repeat(C_SPACE, lineLength + 2));
builder.append(comment);
}
if (comment.contains(S_SAFEPOINT_POLL) || comment.contains(S_SAFEPOINT_POLL_RETURN))
{
builder.append(" *** SAFEPOINT POLL ***");
}
builder.append(S_NEWLINE);
}
else
{
builder.append(S_NEWLINE);
}
return StringUtil.rtrim(builder.toString());
}
}