package com.oracle.svm.core.graal.llvm;
import static com.oracle.svm.core.graal.llvm.util.LLVMIRBuilder.typeOf;
import static com.oracle.svm.core.graal.llvm.util.LLVMUtils.dumpTypes;
import static com.oracle.svm.core.graal.llvm.util.LLVMUtils.dumpValues;
import static com.oracle.svm.core.graal.llvm.util.LLVMUtils.getType;
import static com.oracle.svm.core.graal.llvm.util.LLVMUtils.getVal;
import static org.graalvm.compiler.debug.GraalError.shouldNotReachHere;
import static org.graalvm.compiler.debug.GraalError.unimplemented;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import org.graalvm.compiler.code.CompilationResult;
import org.graalvm.compiler.core.common.CompressEncoding;
import org.graalvm.compiler.core.common.LIRKind;
import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.compiler.core.common.calc.Condition;
import org.graalvm.compiler.core.common.calc.FloatConvert;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.core.common.spi.CodeGenProviders;
import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
import org.graalvm.compiler.core.common.spi.LIRKindTool;
import org.graalvm.compiler.core.common.type.RawPointerStamp;
import org.graalvm.compiler.core.common.type.Stamp;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.lir.LIRFrameState;
import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.LabelRef;
import org.graalvm.compiler.lir.StandardOp;
import org.graalvm.compiler.lir.Variable;
import org.graalvm.compiler.lir.VirtualStackSlot;
import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
import org.graalvm.compiler.lir.gen.LIRGenerationResult;
import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.ParameterNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.cfg.Block;
import org.graalvm.compiler.nodes.type.NarrowOopStamp;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.nativeimage.c.constant.CEnum;
import org.graalvm.util.GuardedAnnotationAccess;
import com.oracle.svm.core.FrameAccess;
import com.oracle.svm.core.ReservedRegisters;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.c.function.CEntryPointBuiltins;
import com.oracle.svm.core.c.function.CEntryPointNativeFunctions;
import com.oracle.svm.core.c.function.CEntryPointOptions;
import com.oracle.svm.core.graal.code.SubstrateCallingConvention;
import com.oracle.svm.core.graal.code.SubstrateCallingConventionType;
import com.oracle.svm.core.graal.code.SubstrateDataBuilder;
import com.oracle.svm.core.graal.code.SubstrateLIRGenerator;
import com.oracle.svm.core.graal.llvm.LLVMFeature.LLVMVersionChecker;
import com.oracle.svm.core.graal.llvm.replacements.LLVMIntrinsicGenerator;
import com.oracle.svm.core.graal.llvm.runtime.LLVMExceptionUnwind;
import com.oracle.svm.core.graal.llvm.util.LLVMIRBuilder;
import com.oracle.svm.core.graal.llvm.util.LLVMIRBuilder.Attribute;
import com.oracle.svm.core.graal.llvm.util.LLVMIRBuilder.GCStrategy;
import com.oracle.svm.core.graal.llvm.util.LLVMIRBuilder.InlineAssemblyConstraint;
import com.oracle.svm.core.graal.llvm.util.LLVMIRBuilder.InlineAssemblyConstraint.Location;
import com.oracle.svm.core.graal.llvm.util.LLVMIRBuilder.InlineAssemblyConstraint.Type;
import com.oracle.svm.core.graal.llvm.util.LLVMIRBuilder.LinkageType;
import com.oracle.svm.core.graal.llvm.util.LLVMOptions;
import com.oracle.svm.core.graal.llvm.util.LLVMStackMapInfo;
import com.oracle.svm.core.graal.llvm.util.LLVMTargetSpecific;
import com.oracle.svm.core.graal.llvm.util.LLVMUtils;
import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMConstant;
import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMKind;
import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMPendingSpecialRegisterRead;
import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMStackSlot;
import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMValueWrapper;
import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMVariable;
import com.oracle.svm.core.graal.meta.SubstrateRegisterConfig;
import com.oracle.svm.core.graal.snippets.CEntryPointSnippets;
import com.oracle.svm.core.heap.ReferenceAccess;
import com.oracle.svm.core.meta.SubstrateObjectConstant;
import com.oracle.svm.core.snippets.SnippetRuntime;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.code.CEntryPointData;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.meta.HostedType;
import com.oracle.svm.shadowed.org.bytedeco.llvm.LLVM.LLVMBasicBlockRef;
import com.oracle.svm.shadowed.org.bytedeco.llvm.LLVM.LLVMTypeRef;
import com.oracle.svm.shadowed.org.bytedeco.llvm.LLVM.LLVMValueRef;
import jdk.vm.ci.code.CallingConvention;
import jdk.vm.ci.code.CodeCacheProvider;
import jdk.vm.ci.code.DebugInfo;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.RegisterValue;
import jdk.vm.ci.code.StackSlot;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.code.site.DataSectionReference;
import jdk.vm.ci.code.site.InfopointReason;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.Constant;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.PlatformKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.Value;
import jdk.vm.ci.meta.ValueKind;
public class LLVMGenerator implements LIRGeneratorTool, SubstrateLIRGenerator {
private static final SubstrateDataBuilder dataBuilder = new SubstrateDataBuilder();
private final Providers providers;
private final CompilationResult compilationResult;
private final LLVMIRBuilder builder;
private final ArithmeticLLVMGenerator arithmetic;
private final LIRKindTool lirKindTool;
private final DebugInfoPrinter debugInfoPrinter;
private final String functionName;
private final boolean isEntryPoint;
private final boolean canModifySpecialRegisters;
private final boolean returnsEnum;
private final boolean returnsCEnum;
private Block currentBlock;
private final Map<AbstractBeginNode, LLVMBasicBlockRef> basicBlockMap = new HashMap<>();
private final Map<Block, LLVMBasicBlockRef> splitBlockEndMap = new HashMap<>();
private final Map<Block, LLVMValueRef[]> specialRegValues = new HashMap<>();
private final Map<Block, LLVMValueRef[]> initialSpecialRegValues = new HashMap<>();
private final Map<Block, LLVMValueRef[]> handlerSpecialRegValues = new HashMap<>();
private final LLVMValueRef[] stackSlots = new LLVMValueRef[SpecialRegister.count()];
private final Map<Constant, String> constants = new HashMap<>();
LLVMGenerator(Providers providers, CompilationResult result, ResolvedJavaMethod method, int debugLevel) {
this.providers = providers;
this.compilationResult = result;
this.builder = new LLVMIRBuilder(method.format("%H.%n"));
this.arithmetic = new ArithmeticLLVMGenerator(builder);
this.lirKindTool = new LLVMUtils.LLVMKindTool(builder);
this.debugInfoPrinter = new DebugInfoPrinter(this, debugLevel);
this.functionName = SubstrateUtil.uniqueShortName(method);
this.isEntryPoint = isEntryPoint(method);
this.canModifySpecialRegisters = canModifySpecialRegisters(method);
ResolvedJavaType returnType = method.getSignature().getReturnType(null).resolve(null);
this.returnsEnum = returnType.isEnum();
this.returnsCEnum = isCEnumType(returnType);
addMainFunction(method);
}
@Override
public CodeGenProviders getProviders() {
return providers;
}
@Override
public MetaAccessProvider getMetaAccess() {
return providers.getMetaAccess();
}
@Override
public CodeCacheProvider getCodeCache() {
return providers.getCodeCache();
}
@Override
public TargetDescription target() {
return getCodeCache().getTarget();
}
@Override
public SubstrateRegisterConfig getRegisterConfig() {
return (SubstrateRegisterConfig) getCodeCache().getRegisterConfig();
}
@Override
public ForeignCallsProvider getForeignCalls() {
return providers.getForeignCalls();
}
CompilationResult getCompilationResult() {
return compilationResult;
}
public LLVMIRBuilder getBuilder() {
return builder;
}
@Override
public ArithmeticLIRGeneratorTool getArithmetic() {
return arithmetic;
}
DebugInfoPrinter getDebugInfoPrinter() {
return debugInfoPrinter;
}
String getFunctionName() {
return functionName;
}
boolean isEntryPoint() {
return isEntryPoint;
}
private void addMainFunction(ResolvedJavaMethod method) {
builder.setMainFunction(functionName, getLLVMFunctionType(method, true));
builder.setFunctionLinkage(LinkageType.External);
builder.setFunctionAttribute(Attribute.NoInline);
builder.setFunctionAttribute(Attribute.NoRedZone);
builder.setFunctionAttribute(Attribute.NoRealignStack);
builder.setGarbageCollector(GCStrategy.CompressedPointers);
builder.setPersonalityFunction(getFunction(LLVMExceptionUnwind.getPersonalityStub(getMetaAccess())));
if (isEntryPoint) {
builder.addAlias(SubstrateUtil.mangleName(functionName));
Object entryPointData = ((HostedMethod) method).getWrapped().getEntryPointData();
if (entryPointData instanceof CEntryPointData) {
CEntryPointData cEntryPointData = (CEntryPointData) entryPointData;
if (cEntryPointData.getPublishAs() != CEntryPointOptions.Publish.NotPublished) {
String entryPointSymbolName = cEntryPointData.getSymbolName();
assert !entryPointSymbolName.isEmpty();
builder.addAlias(entryPointSymbolName);
}
}
}
}
LLVMValueRef getFunction(ResolvedJavaMethod method) {
LLVMTypeRef functionType = getLLVMFunctionType(method, false);
return builder.getFunction(getFunctionName(method), functionType);
}
byte[] getBitcode() {
assert builder.verifyBitcode();
byte[] bitcode = builder.getBitcode();
builder.close();
return bitcode;
}
private static String getFunctionName(ResolvedJavaMethod method) {
return SubstrateUtil.uniqueShortName(method);
}
private static boolean isEntryPoint(ResolvedJavaMethod method) {
return ((HostedMethod) method).isEntryPoint();
}
private boolean canModifySpecialRegisters(ResolvedJavaMethod method) {
CEntryPointOptions entryPointOptions = GuardedAnnotationAccess.getAnnotation(method, CEntryPointOptions.class);
return (entryPointOptions != null) && entryPointOptions.prologue() == CEntryPointOptions.NoPrologue.class ||
method.getDeclaringClass().equals(getMetaAccess().lookupJavaType(CEntryPointSnippets.class)) ||
method.getDeclaringClass().equals(getMetaAccess().lookupJavaType(CEntryPointNativeFunctions.class)) ||
method.getDeclaringClass().equals(getMetaAccess().lookupJavaType(CEntryPointBuiltins.class));
}
void appendBasicBlock(Block block) {
LLVMBasicBlockRef basicBlock = builder.appendBasicBlock(block.toString());
basicBlockMap.put(block.getBeginNode(), basicBlock);
}
void beginBlock(Block block) {
currentBlock = block;
builder.positionAtEnd(getBlock(block));
}
void resumeBlock(Block block) {
currentBlock = block;
builder.positionAtEnd(getBlockEnd(block));
}
void editBlock(Block block) {
currentBlock = block;
builder.positionBeforeTerminator(getBlockEnd(block));
}
@Override
public AbstractBlockBase<?> getCurrentBlock() {
return currentBlock;
}
LLVMBasicBlockRef getBlock(Block block) {
return getBlock(block.getBeginNode());
}
LLVMBasicBlockRef getBlock(AbstractBeginNode begin) {
return basicBlockMap.get(begin);
}
LLVMBasicBlockRef getBlockEnd(Block block) {
return (splitBlockEndMap.containsKey(block)) ? splitBlockEndMap.get(block) : getBlock(block);
}
@Override
public LIRKind getLIRKind(Stamp stamp) {
return stamp.getLIRKind(lirKindTool);
}
@Override
public LIRKind getValueKind(JavaKind javaKind) {
return getLIRKind(StampFactory.forKind(javaKind));
}
LLVMTypeRef getLLVMType(Stamp stamp) {
if (stamp instanceof RawPointerStamp) {
return builder.rawPointerType();
}
return getLLVMType(getTypeKind(stamp.javaType(getMetaAccess()), false), stamp instanceof NarrowOopStamp);
}
LLVMTypeRef getLLVMStackType(JavaKind kind) {
return getLLVMType(kind.getStackKind(), false);
}
JavaKind getTypeKind(ResolvedJavaType type, boolean forMainFunction) {
if (forMainFunction && isEntryPoint && isCEnumType(type)) {
return JavaKind.Int;
}
return ((HostedType) type).getStorageKind();
}
private LLVMTypeRef getLLVMType(JavaKind kind, boolean compressedObjects) {
switch (kind) {
case Boolean:
return builder.booleanType();
case Byte:
return builder.byteType();
case Short:
return builder.shortType();
case Char:
return builder.charType();
case Int:
return builder.intType();
case Float:
return builder.floatType();
case Long:
return builder.longType();
case Double:
return builder.doubleType();
case Object:
return builder.objectType(compressedObjects);
case Void:
return builder.voidType();
case Illegal:
default:
throw shouldNotReachHere("Illegal type");
}
}
private static JavaKind getJavaKind(LLVMTypeRef type) {
if (LLVMIRBuilder.isBooleanType(type)) {
return JavaKind.Boolean;
} else if (LLVMIRBuilder.isByteType(type)) {
return JavaKind.Byte;
} else if (LLVMIRBuilder.isShortType(type)) {
return JavaKind.Short;
} else if (LLVMIRBuilder.isCharType(type)) {
return JavaKind.Char;
} else if (LLVMIRBuilder.isIntType(type)) {
return JavaKind.Int;
} else if (LLVMIRBuilder.isLongType(type)) {
return JavaKind.Long;
} else if (LLVMIRBuilder.isFloatType(type)) {
return JavaKind.Float;
} else if (LLVMIRBuilder.isDoubleType(type)) {
return JavaKind.Double;
} else if (LLVMIRBuilder.isObjectType(type)) {
return JavaKind.Object;
} else if (LLVMIRBuilder.isVoidType(type)) {
return JavaKind.Void;
} else {
throw shouldNotReachHere("Unknown LLVM type");
}
}
private LLVMTypeRef getLLVMFunctionType(ResolvedJavaMethod method, boolean forMainFunction) {
return builder.functionType(getLLVMFunctionReturnType(method, forMainFunction), getLLVMFunctionArgTypes(method, forMainFunction));
}
LLVMTypeRef getLLVMFunctionPointerType(ResolvedJavaMethod method) {
return builder.functionPointerType(getLLVMFunctionReturnType(method, false), getLLVMFunctionArgTypes(method, false));
}
LLVMTypeRef getLLVMFunctionReturnType(ResolvedJavaMethod method, boolean forMainFunction) {
ResolvedJavaType returnType = method.getSignature().getReturnType(null).resolve(null);
LLVMTypeRef llvmReturnType = getLLVMStackType(getTypeKind(returnType, forMainFunction));
if (LLVMOptions.ReturnSpecialRegs.getValue() && !(forMainFunction && isEntryPoint)) {
boolean voidReturnType = LLVMIRBuilder.isVoidType(llvmReturnType);
LLVMTypeRef[] returnTypes = new LLVMTypeRef[SpecialRegister.count() + (voidReturnType ? 0 : 1)];
for (SpecialRegister reg : SpecialRegister.registers()) {
returnTypes[reg.index] = builder.wordType();
}
if (!voidReturnType) {
returnTypes[SpecialRegister.count()] = llvmReturnType;
}
llvmReturnType = builder.structType(returnTypes);
}
return llvmReturnType;
}
boolean isVoidReturnType(LLVMTypeRef returnType) {
if (LLVMOptions.ReturnSpecialRegs.getValue()) {
return LLVMIRBuilder.countElementTypes(returnType) == SpecialRegister.count();
}
return LLVMIRBuilder.isVoidType(returnType);
}
private LLVMTypeRef[] getLLVMFunctionArgTypes(ResolvedJavaMethod method, boolean forMainFunction) {
ResolvedJavaType receiver = method.hasReceiver() ? method.getDeclaringClass() : null;
JavaType[] javaParameterTypes = method.getSignature().toParameterTypes(receiver);
LLVMTypeRef[] parameterTypes = Arrays.stream(javaParameterTypes).map(type -> getLLVMStackType(getTypeKind(type.resolve(null), forMainFunction))).toArray(LLVMTypeRef[]::new);
LLVMTypeRef[] newParameterTypes = parameterTypes;
if (!isEntryPoint(method) && SpecialRegister.count() > 0) {
newParameterTypes = new LLVMTypeRef[SpecialRegister.count() + parameterTypes.length];
for (SpecialRegister reg : SpecialRegister.registers()) {
newParameterTypes[reg.index] = !LLVMOptions.ReturnSpecialRegs.getValue() && canModifySpecialRegisters(method) ? builder.pointerType(builder.wordType()) : builder.wordType();
}
System.arraycopy(parameterTypes, 0, newParameterTypes, SpecialRegister.count(), parameterTypes.length);
}
return newParameterTypes;
}
private LLVMTypeRef prependArgumentTypes(LLVMTypeRef functionType, int prefixTypes, LLVMTypeRef... typesToAdd) {
LLVMTypeRef returnType = LLVMIRBuilder.getReturnType(functionType);
boolean varargs = LLVMIRBuilder.isFunctionVarArg(functionType);
LLVMTypeRef[] oldTypes = LLVMIRBuilder.getParamTypes(functionType);
LLVMTypeRef[] newTypes = new LLVMTypeRef[oldTypes.length + typesToAdd.length];
System.arraycopy(oldTypes, 0, newTypes, 0, prefixTypes);
System.arraycopy(typesToAdd, 0, newTypes, prefixTypes, typesToAdd.length);
System.arraycopy(oldTypes, prefixTypes, newTypes, prefixTypes + typesToAdd.length, oldTypes.length - prefixTypes);
return builder.functionType(returnType, varargs, newTypes);
}
private static boolean isCEnumType(ResolvedJavaType type) {
return type.isEnum() && GuardedAnnotationAccess.isAnnotationPresent(type, CEnum.class);
}
@Override
public Value emitConstant(LIRKind kind, Constant constant) {
boolean uncompressedObject = isUncompressedObjectKind(kind);
LLVMTypeRef actualType = uncompressedObject ? builder.objectType(true) : ((LLVMKind) kind.getPlatformKind()).get();
LLVMValueRef value = emitLLVMConstant(actualType, (JavaConstant) constant);
Value val = new LLVMConstant(value, constant);
return uncompressedObject ? emitUncompress(val, ReferenceAccess.singleton().getCompressEncoding(), false) : val;
}
@Override
public Value emitJavaConstant(JavaConstant constant) {
assert constant.getJavaKind() != JavaKind.Object;
LLVMValueRef value = emitLLVMConstant(getLLVMType(constant.getJavaKind(), false), constant);
return new LLVMConstant(value, constant);
}
LLVMValueRef emitLLVMConstant(LLVMTypeRef type, JavaConstant constant) {
switch (getJavaKind(type)) {
case Boolean:
return builder.constantBoolean(constant.asBoolean());
case Byte:
return builder.constantByte((byte) constant.asInt());
case Short:
return builder.constantShort((short) constant.asInt());
case Char:
return builder.constantChar((char) constant.asInt());
case Int:
return builder.constantInt(constant.asInt());
case Long:
return builder.constantLong(constant.asLong());
case Float:
return builder.constantFloat(constant.asFloat());
case Double:
return builder.constantDouble(constant.asDouble());
case Object:
if (constant.isNull()) {
return builder.constantNull(builder.objectType(LLVMIRBuilder.isCompressedPointerType(type)));
} else {
return builder.buildLoad(getLLVMPlaceholderForConstant(constant), builder.objectType(LLVMIRBuilder.isCompressedPointerType(type)));
}
default:
throw shouldNotReachHere(dumpTypes("unsupported constant type", type));
}
}
@Override
public AllocatableValue emitLoadConstant(ValueKind<?> kind, Constant constant) {
LLVMValueRef value = builder.buildLoad(getLLVMPlaceholderForConstant(constant), ((LLVMKind) kind.getPlatformKind()).get());
AllocatableValue rawConstant = new LLVMVariable(value);
if (SubstrateOptions.SpawnIsolates.getValue() && ((LIRKind) kind).isReference(0) && !((LIRKind) kind).isCompressedReference(0)) {
return (AllocatableValue) emitUncompress(rawConstant, ReferenceAccess.singleton().getCompressEncoding(), false);
}
return rawConstant;
}
private long nextConstantId = 0L;
private LLVMValueRef getLLVMPlaceholderForConstant(Constant constant) {
String symbolName = constants.get(constant);
boolean uncompressedObject = isUncompressedObjectConstant(constant);
if (symbolName == null) {
symbolName = "constant_" + functionName + "#" + nextConstantId++;
constants.put(constant, symbolName);
Constant storedConstant = uncompressedObject ? ((SubstrateObjectConstant) constant).compress() : constant;
DataSectionReference reference = compilationResult.getDataSection().insertData(dataBuilder.createDataItem(storedConstant));
compilationResult.recordDataPatchWithNote(0, reference, symbolName);
}
return builder.getExternalObject(symbolName, isUncompressedObjectConstant(constant));
}
private static boolean isUncompressedObjectConstant(Constant constant) {
return SubstrateOptions.SpawnIsolates.getValue() && constant instanceof SubstrateObjectConstant && !((SubstrateObjectConstant) constant).isCompressed();
}
private static boolean isUncompressedObjectKind(LIRKind kind) {
return SubstrateOptions.SpawnIsolates.getValue() && kind.isReference(0) && !kind.isCompressedReference(0);
}
@Override
public boolean canInlineConstant(Constant constant) {
return false;
}
@Override
public boolean mayEmbedConstantLoad(Constant constant) {
return false;
}
@Override
public <K extends ValueKind<K>> K toRegisterKind(K kind) {
throw unimplemented("only needed when emitting LIR constants");
}
@Override
public void emitMoveConstant(AllocatableValue dst, Constant src) {
throw unimplemented("the LLVM backend doesn't need to move constants");
}
@Override
public Variable newVariable(ValueKind<?> kind) {
return new LLVMVariable(kind);
}
@Override
public AllocatableValue asAllocatable(Value value) {
return (AllocatableValue) value;
}
@Override
public Variable emitMove(Value input) {
if (input instanceof LLVMVariable) {
return (LLVMVariable) input;
} else if (input instanceof LLVMValueWrapper) {
return new LLVMVariable(getVal(input));
}
throw shouldNotReachHere("Unknown move input");
}
@Override
public void emitMove(AllocatableValue dst, Value src) {
LLVMValueRef source = getVal(src);
LLVMTypeRef sourceType = typeOf(source);
LLVMTypeRef destType = ((LLVMKind) dst.getPlatformKind()).get();
if (LLVMIRBuilder.isObjectType(destType) && LLVMIRBuilder.isWordType(sourceType)) {
source = builder.buildIntToPtr(source, destType);
} else if (LLVMIRBuilder.isWordType(destType) && LLVMIRBuilder.isObjectType(sourceType)) {
source = builder.buildPtrToInt(source);
}
((LLVMVariable) dst).set(source);
}
@Override
public Variable emitConditionalMove(PlatformKind cmpKind, Value leftVal, Value rightVal, Condition cond, boolean unorderedIsTrue, Value trueVal, Value falseVal) {
LLVMValueRef condition = builder.buildCompare(cond, getVal(leftVal), getVal(rightVal), unorderedIsTrue);
LLVMValueRef select;
LLVMValueRef trueValue = getVal(trueVal);
LLVMValueRef falseValue = getVal(falseVal);
if (LLVMVersionChecker.useExplicitSelects() && LLVMIRBuilder.isObjectType(typeOf(trueValue))) {
select = buildExplicitSelect(condition, trueValue, falseValue);
} else {
select = builder.buildSelect(condition, trueValue, falseValue);
}
return new LLVMVariable(select);
}
Variable emitIsNullMove(Value value, Value trueValue, Value falseValue) {
LLVMValueRef isNull = builder.buildIsNull(getVal(value));
LLVMValueRef select = builder.buildSelect(isNull, getVal(trueValue), getVal(falseValue));
return new LLVMVariable(select);
}
@Override
public Variable emitIntegerTestMove(Value left, Value right, Value trueValue, Value falseValue) {
LLVMValueRef and = builder.buildAnd(getVal(left), getVal(right));
LLVMValueRef isNull = builder.buildIsNull(and);
LLVMValueRef select = builder.buildSelect(isNull, getVal(trueValue), getVal(falseValue));
return new LLVMVariable(select);
}
private LLVMValueRef buildExplicitSelect(LLVMValueRef condition, LLVMValueRef trueVal, LLVMValueRef falseVal) {
LLVMBasicBlockRef trueBlock = builder.appendBasicBlock(currentBlock.toString() + "_select_true");
LLVMBasicBlockRef falseBlock = builder.appendBasicBlock(currentBlock.toString() + "_select_false");
LLVMBasicBlockRef mergeBlock = builder.appendBasicBlock(currentBlock.toString() + "_select_end");
splitBlockEndMap.put(currentBlock, mergeBlock);
assert LLVMIRBuilder.compatibleTypes(typeOf(trueVal), typeOf(falseVal));
builder.buildIf(condition, trueBlock, falseBlock);
builder.positionAtEnd(trueBlock);
builder.buildBranch(mergeBlock);
builder.positionAtEnd(falseBlock);
builder.buildBranch(mergeBlock);
builder.positionAtEnd(mergeBlock);
LLVMValueRef[] incomingValues = new LLVMValueRef[]{trueVal, falseVal};
LLVMBasicBlockRef[] incomingBlocks = new LLVMBasicBlockRef[]{trueBlock, falseBlock};
return builder.buildPhi(typeOf(trueVal), incomingValues, incomingBlocks);
}
@Override
public Variable emitByteSwap(Value operand) {
LLVMValueRef byteSwap = builder.buildBswap(getVal(operand));
return new LLVMVariable(byteSwap);
}
@Override
public void emitMembar(int barriers) {
builder.buildFence();
}
@Override
public Value emitAtomicReadAndWrite(Value address, ValueKind<?> valueKind, Value newValue) {
LLVMValueRef atomicRMW = builder.buildAtomicXchg(getVal(address), getVal(newValue));
return new LLVMVariable(atomicRMW);
}
@Override
public Value emitAtomicReadAndAdd(Value address, ValueKind<?> valueKind, Value delta) {
LLVMValueRef atomicRMW = builder.buildAtomicAdd(getVal(address), getVal(delta));
return new LLVMVariable(atomicRMW);
}
@Override
public Variable emitLogicCompareAndSwap(LIRKind accessKind, Value address, Value expectedValue, Value newValue, Value trueValue, Value falseValue, boolean useBarriers) {
LLVMValueRef success = buildCmpxchg(getVal(address), getVal(expectedValue), getVal(newValue), false);
LLVMValueRef result = builder.buildSelect(success, getVal(trueValue), getVal(falseValue));
return new LLVMVariable(result);
}
@Override
public Value emitValueCompareAndSwap(LIRKind accessKind, Value address, Value expectedValue, Value newValue, boolean useBarriers) {
LLVMValueRef result = buildCmpxchg(getVal(address), getVal(expectedValue), getVal(newValue), true);
return new LLVMVariable(result);
}
private LLVMValueRef buildCmpxchg(LLVMValueRef address, LLVMValueRef expectedValue, LLVMValueRef newValue, boolean returnValue) {
LLVMTypeRef expectedType = LLVMIRBuilder.typeOf(expectedValue);
LLVMTypeRef newType = LLVMIRBuilder.typeOf(newValue);
assert LLVMIRBuilder.compatibleTypes(expectedType, newType) : dumpValues("invalid cmpxchg arguments", expectedValue, newValue);
LLVMValueRef castedAddress = builder.buildBitcast(address, builder.pointerType(expectedType, LLVMIRBuilder.isObjectType(typeOf(address)), false));
return builder.buildCmpxchg(castedAddress, expectedValue, newValue, returnValue);
}
@Override
public Variable emitReadRegister(Register register, ValueKind<?> kind) {
LLVMValueRef value;
if (register.equals(ReservedRegisters.singleton().getThreadRegister())) {
if (isEntryPoint || canModifySpecialRegisters) {
return new LLVMPendingSpecialRegisterRead(this, SpecialRegister.ThreadPointer);
}
value = getSpecialRegister(SpecialRegister.ThreadPointer);
} else if (register.equals(ReservedRegisters.singleton().getHeapBaseRegister())) {
if (isEntryPoint || canModifySpecialRegisters) {
return new LLVMPendingSpecialRegisterRead(this, SpecialRegister.HeapBase);
}
value = getSpecialRegister(SpecialRegister.HeapBase);
} else if (register.equals(ReservedRegisters.singleton().getFrameRegister())) {
value = builder.buildReadRegister(builder.register(ReservedRegisters.singleton().getFrameRegister().name));
} else {
throw VMError.shouldNotReachHere();
}
return new LLVMVariable(value);
}
@Override
public void emitWriteRegister(Register dst, Value src, ValueKind<?> kind) {
if (dst.equals(ReservedRegisters.singleton().getThreadRegister())) {
VMError.guarantee(isEntryPoint || canModifySpecialRegisters, "Can only write to registers in a method where it is expected.");
if (LLVMOptions.ReturnSpecialRegs.getValue()) {
setSpecialRegisterValue(SpecialRegister.ThreadPointer, getVal(src));
} else {
builder.buildStore(getVal(src), getSpecialRegisterPointer(SpecialRegister.ThreadPointer));
}
return;
} else if (dst.equals(ReservedRegisters.singleton().getHeapBaseRegister())) {
VMError.guarantee(isEntryPoint || canModifySpecialRegisters, "Can only write to registers in a method where it is expected.");
if (LLVMOptions.ReturnSpecialRegs.getValue()) {
setSpecialRegisterValue(SpecialRegister.HeapBase, getVal(src));
} else {
builder.buildStore(getVal(src), getSpecialRegisterPointer(SpecialRegister.HeapBase));
}
return;
}
throw VMError.shouldNotReachHere();
}
@Override
public Variable load(Value value) {
LLVMValueRef load = builder.buildPtrToInt(getVal(value));
return new LLVMVariable(load);
}
@Override
public void emitPrefetchAllocate(Value address) {
builder.buildPrefetch(getVal(address));
}
@Override
public Value emitCompress(Value pointer, CompressEncoding encoding, boolean nonNull) {
LLVMValueRef heapBase = getSpecialRegister(SpecialRegister.HeapBase);
return new LLVMVariable(builder.buildCompress(getVal(pointer), heapBase, nonNull, encoding.getShift()));
}
@Override
public Value emitUncompress(Value pointer, CompressEncoding encoding, boolean nonNull) {
LLVMValueRef heapBase = getSpecialRegister(SpecialRegister.HeapBase);
return new LLVMVariable(builder.buildUncompress(getVal(pointer), heapBase, nonNull, encoding.getShift()));
}
@Override
public VirtualStackSlot allocateStackSlots(int slots) {
builder.positionAtStart();
LLVMValueRef alloca = builder.buildArrayAlloca(builder.wordType(), slots);
builder.positionAtEnd(getBlockEnd(currentBlock));
return new LLVMStackSlot(alloca);
}
@Override
public Variable emitAddress(AllocatableValue stackslot) {
if (stackslot instanceof LLVMStackSlot) {
return new LLVMVariable(builder.buildPtrToInt(getVal(stackslot)));
}
throw shouldNotReachHere("Unknown address type");
}
void allocateRegisterSlots() {
if (!isEntryPoint) {
return;
}
for (SpecialRegister reg : SpecialRegister.registers()) {
stackSlots[reg.index] = builder.buildAlloca(builder.wordType());
}
}
@Override
public Value emitReadCallerStackPointer(Stamp wordStamp) {
LLVMValueRef basePointer = builder.buildFrameAddress(builder.constantInt(0));
LLVMValueRef callerSP = builder.buildAdd(builder.buildPtrToInt(basePointer), builder.constantLong(16));
return new LLVMVariable(callerSP);
}
@Override
public Value emitReadReturnAddress(Stamp wordStamp, int returnAddressSize) {
LLVMValueRef returnAddress = builder.buildReturnAddress(builder.constantInt(0));
return new LLVMVariable(builder.buildPtrToInt(returnAddress));
}
static final AtomicLong nextPatchpointId = new AtomicLong(0);
LLVMValueRef buildStatepointCall(LLVMValueRef callee, boolean nativeABI, long statepointId, LLVMValueRef... args) {
LLVMValueRef result;
result = builder.buildCall(callee, args);
builder.setCallSiteAttribute(result, Attribute.StatepointID, Long.toString(statepointId));
if (!nativeABI && LLVMOptions.ReturnSpecialRegs.getValue()) {
for (SpecialRegister reg : SpecialRegister.registers()) {
setSpecialRegisterValue(reg, builder.buildExtractValue(result, reg.index));
}
int numReturnValues = LLVMIRBuilder.countElementTypes(typeOf(result));
return numReturnValues > SpecialRegister.count() ? builder.buildExtractValue(result, SpecialRegister.count()) : result;
}
return result;
}
LLVMValueRef buildStatepointInvoke(LLVMValueRef callee, boolean nativeABI, LLVMBasicBlockRef successor, LLVMBasicBlockRef handler, long statepointId, LLVMValueRef... args) {
LLVMBasicBlockRef successorBlock;
LLVMBasicBlockRef handlerBlock;
if (!nativeABI && LLVMOptions.ReturnSpecialRegs.getValue()) {
successorBlock = builder.appendBasicBlock(currentBlock.toString() + "_invoke_successor");
handlerBlock = builder.appendBasicBlock(currentBlock.toString() + "_invoke_handler");
splitBlockEndMap.put(currentBlock, successorBlock);
} else {
successorBlock = successor;
handlerBlock = handler;
}
LLVMValueRef result = builder.buildInvoke(callee, successorBlock, handlerBlock, args);
builder.setCallSiteAttribute(result, Attribute.StatepointID, Long.toString(statepointId));
if (!nativeABI && LLVMOptions.ReturnSpecialRegs.getValue()) {
builder.positionAtEnd(handlerBlock);
builder.buildLandingPad();
for (SpecialRegister reg : SpecialRegister.registers()) {
setHandlerSpecialRegisterValue(reg, getSpecialRegisterValue(reg));
}
builder.buildBranch(handler);
builder.positionAtEnd(successorBlock);
int numReturnValues = LLVMIRBuilder.countElementTypes(typeOf(result));
for (SpecialRegister reg : SpecialRegister.registers()) {
assert reg.index < numReturnValues;
setSpecialRegisterValue(reg, builder.buildExtractValue(result, reg.index));
}
result = numReturnValues > SpecialRegister.count() ? builder.buildExtractValue(result, SpecialRegister.count()) : result;
builder.buildBranch(successor);
}
return result;
}
@Override
public Variable emitForeignCall(ForeignCallLinkage linkage, LIRFrameState state, Value... arguments) {
return emitForeignCall(linkage, state, null, null, arguments);
}
public Variable emitForeignCall(ForeignCallLinkage linkage, LIRFrameState state, LLVMBasicBlockRef successor, LLVMBasicBlockRef handler, Value... arguments) {
ResolvedJavaMethod targetMethod = ((SnippetRuntime.SubstrateForeignCallDescriptor) linkage.getDescriptor()).findMethod(getMetaAccess());
DebugInfo debugInfo = null;
if (state != null) {
state.initDebugInfo(null, false);
debugInfo = state.debugInfo();
}
long patchpointId = nextPatchpointId.getAndIncrement();
compilationResult.recordCall(NumUtil.safeToInt(patchpointId), 0, targetMethod, debugInfo, true);
LLVMValueRef callee = getFunction(targetMethod);
LLVMValueRef[] args = Arrays.stream(arguments).map(LLVMUtils::getVal).toArray(LLVMValueRef[]::new);
CallingConvention.Type callType = ((SubstrateCallingConvention) linkage.getOutgoingCallingConvention()).getType();
LLVMValueRef[] callArguments = getCallArguments(args, callType, targetMethod);
LLVMValueRef call;
boolean nativeABI = ((SubstrateCallingConventionType) callType).nativeABI;
if (successor == null && handler == null) {
call = buildStatepointCall(callee, nativeABI, patchpointId, callArguments);
} else {
assert successor != null && handler != null;
call = buildStatepointInvoke(callee, nativeABI, successor, handler, patchpointId, callArguments);
}
return (isVoidReturnType(getLLVMFunctionReturnType(targetMethod, false))) ? null : new LLVMVariable(call);
}
LLVMValueRef[] getCallArguments(LLVMValueRef[] args, CallingConvention.Type callType, ResolvedJavaMethod targetMethod) {
LLVMValueRef[] newArgs = args;
if (!((SubstrateCallingConventionType) callType).nativeABI && SpecialRegister.hasRegisters()) {
newArgs = new LLVMValueRef[SpecialRegister.count() + args.length];
for (SpecialRegister reg : SpecialRegister.registers()) {
newArgs[reg.index] = getSpecialRegisterArgument(reg, targetMethod);
}
System.arraycopy(args, 0, newArgs, SpecialRegister.count(), args.length);
}
return newArgs;
}
LLVMTypeRef[] getUnknownCallArgumentTypes(LLVMTypeRef[] types, CallingConvention.Type callType) {
LLVMTypeRef[] newTypes = types;
if (!((SubstrateCallingConventionType) callType).nativeABI && SpecialRegister.count() > 0) {
newTypes = new LLVMTypeRef[SpecialRegister.count() + types.length];
for (SpecialRegister reg : SpecialRegister.registers()) {
newTypes[reg.index] = builder.wordType();
}
System.arraycopy(types, 0, newTypes, SpecialRegister.count(), types.length);
}
return newTypes;
}
public static final String JNI_WRAPPER_BASE_NAME = "__llvm_jni_wrapper_";
LLVMValueRef createJNIWrapper(LLVMValueRef callee, boolean nativeABI, int numArgs, int anchorIPOffset) {
LLVMTypeRef calleeType = LLVMIRBuilder.getElementType(LLVMIRBuilder.typeOf(callee));
String wrapperName = JNI_WRAPPER_BASE_NAME + LLVMIRBuilder.intrinsicType(calleeType) + (nativeABI ? "_native" : "");
LLVMValueRef transitionWrapper = builder.getNamedFunction(wrapperName);
if (transitionWrapper == null) {
try (LLVMIRBuilder tempBuilder = new LLVMIRBuilder(builder)) {
LLVMTypeRef wrapperType = prependArgumentTypes(calleeType, nativeABI ? 0 : SpecialRegister.count(), tempBuilder.rawPointerType(), LLVMIRBuilder.typeOf(callee));
transitionWrapper = tempBuilder.addFunction(wrapperName, wrapperType);
LLVMIRBuilder.setLinkage(transitionWrapper, LinkageType.LinkOnce);
tempBuilder.setGarbageCollector(transitionWrapper, GCStrategy.CompressedPointers);
tempBuilder.setFunctionAttribute(transitionWrapper, Attribute.NoInline);
LLVMBasicBlockRef block = tempBuilder.appendBasicBlock(transitionWrapper, "main");
tempBuilder.positionAtEnd(block);
LLVMValueRef anchor = LLVMIRBuilder.getParam(transitionWrapper, 0 + (nativeABI ? 0 : SpecialRegister.count()));
LLVMValueRef lastIPAddr = tempBuilder.buildGEP(anchor, tempBuilder.constantInt(anchorIPOffset));
LLVMValueRef callIP = tempBuilder.buildReturnAddress(tempBuilder.constantInt(0));
LLVMValueRef castedLastIPAddr = tempBuilder.buildBitcast(lastIPAddr, tempBuilder.pointerType(tempBuilder.rawPointerType()));
tempBuilder.buildStore(callIP, castedLastIPAddr);
LLVMValueRef[] args = new LLVMValueRef[numArgs];
for (int i = 0; i < numArgs; ++i) {
if (!nativeABI && i < SpecialRegister.count()) {
args[i] = LLVMIRBuilder.getParam(transitionWrapper, i);
} else {
args[i] = LLVMIRBuilder.getParam(transitionWrapper, i + 2);
}
}
LLVMValueRef target = LLVMIRBuilder.getParam(transitionWrapper, 1 + (nativeABI ? 0 : SpecialRegister.count()));
LLVMValueRef ret = tempBuilder.buildCall(target, args);
tempBuilder.setCallSiteAttribute(ret, Attribute.GCLeafFunction);
if (LLVMIRBuilder.isVoidType(LLVMIRBuilder.getReturnType(calleeType))) {
tempBuilder.buildRetVoid();
} else {
tempBuilder.buildRet(ret);
}
}
}
return transitionWrapper;
}
void createJNITrampoline(RegisterValue threadArg, int threadIsolateOffset, RegisterValue methodIdArg, int methodObjEntryPointOffset) {
builder.setFunctionAttribute(Attribute.Naked);
LLVMBasicBlockRef block = builder.appendBasicBlock("main");
builder.positionAtEnd(block);
long startPatchpointId = LLVMGenerator.nextPatchpointId.getAndIncrement();
builder.buildStackmap(builder.constantLong(startPatchpointId));
compilationResult.recordInfopoint(NumUtil.safeToInt(startPatchpointId), null, InfopointReason.METHOD_START);
LLVMValueRef jumpAddressAddress;
if (SubstrateOptions.SpawnIsolates.getValue()) {
LLVMValueRef thread = buildInlineGetRegister(threadArg.getRegister().name);
LLVMValueRef heapBaseAddress = builder.buildGEP(builder.buildIntToPtr(thread, builder.rawPointerType()), builder.constantInt(threadIsolateOffset));
LLVMValueRef heapBase = builder.buildLoad(heapBaseAddress, builder.rawPointerType());
LLVMValueRef methodId = buildInlineGetRegister(methodIdArg.getRegister().name);
LLVMValueRef methodBase = builder.buildGEP(builder.buildIntToPtr(heapBase, builder.rawPointerType()), builder.buildPtrToInt(methodId));
jumpAddressAddress = builder.buildGEP(methodBase, builder.constantInt(methodObjEntryPointOffset));
} else {
LLVMValueRef methodBase = buildInlineGetRegister(methodIdArg.getRegister().name);
jumpAddressAddress = builder.buildGEP(builder.buildIntToPtr(methodBase, builder.rawPointerType()), builder.constantInt(methodObjEntryPointOffset));
}
LLVMValueRef jumpAddress = builder.buildLoad(jumpAddressAddress, builder.rawPointerType());
buildInlineJump(jumpAddress);
builder.buildUnreachable();
}
@Override
public void emitReturn(JavaKind javaKind, Value input) {
if (javaKind == JavaKind.Void) {
debugInfoPrinter.printRetVoid();
if (LLVMOptions.ReturnSpecialRegs.getValue() && !isEntryPoint) {
LLVMTypeRef[] retTypes = new LLVMTypeRef[SpecialRegister.count()];
LLVMValueRef[] retValues = new LLVMValueRef[SpecialRegister.count()];
for (SpecialRegister reg : SpecialRegister.registers()) {
retTypes[reg.index] = builder.wordType();
retValues[reg.index] = getSpecialRegisterValue(reg);
}
LLVMValueRef retStruct = builder.constantNull(builder.structType(retTypes));
for (int i = 0; i < retValues.length; ++i) {
retStruct = builder.buildInsertValue(retStruct, i, retValues[i]);
}
builder.buildRet(retStruct);
} else {
builder.buildRetVoid();
}
} else {
debugInfoPrinter.printRet(javaKind, input);
LLVMValueRef retVal = getVal(input);
if (javaKind == JavaKind.Int) {
assert LLVMIRBuilder.isIntegerType(typeOf(retVal));
retVal = arithmetic.emitIntegerConvert(retVal, builder.intType());
} else if (returnsEnum && javaKind == FrameAccess.getWordKind()) {
LLVMValueRef result;
if (returnsCEnum) {
result = builder.buildTrunc(retVal, JavaKind.Int.getBitCount());
} else {
result = builder.buildIntToPtr(retVal, builder.objectType(false));
}
retVal = result;
}
if (LLVMOptions.ReturnSpecialRegs.getValue() && !isEntryPoint) {
LLVMTypeRef[] retTypes = new LLVMTypeRef[SpecialRegister.count() + 1];
LLVMValueRef[] retValues = new LLVMValueRef[SpecialRegister.count() + 1];
for (SpecialRegister reg : SpecialRegister.registers()) {
retTypes[reg.index] = builder.wordType();
retValues[reg.index] = getSpecialRegisterValue(reg);
}
retTypes[SpecialRegister.count()] = LLVMIRBuilder.typeOf(retVal);
retValues[SpecialRegister.count()] = retVal;
LLVMValueRef retStruct = builder.constantNull(builder.structType(retTypes));
for (int i = 0; i < retValues.length; ++i) {
retStruct = builder.buildInsertValue(retStruct, i, retValues[i]);
}
retVal = retStruct;
}
builder.buildRet(retVal);
}
}
@Override
public void emitJump(LabelRef label) {
builder.buildBranch(getBlock((Block) label.getTargetBlock()));
}
@Override
public void emitDeadEnd() {
builder.buildUnreachable();
}
@Override
public void emitBlackhole(Value operand) {
builder.buildStackmap(builder.constantLong(LLVMStackMapInfo.DEFAULT_PATCHPOINT_ID), getVal(operand));
}
@Override
public void emitPause() {
}
private void buildInlineJump(LLVMValueRef address) {
LLVMTypeRef inlineAsmType = builder.functionType(builder.voidType(), builder.rawPointerType());
String asmSnippet = LLVMTargetSpecific.get().getJumpInlineAsm();
InlineAssemblyConstraint inputConstraint = new InlineAssemblyConstraint(Type.Input, Location.register());
LLVMValueRef jump = builder.buildInlineAsm(inlineAsmType, asmSnippet, true, false, inputConstraint);
LLVMValueRef call = builder.buildCall(jump, address);
builder.setCallSiteAttribute(call, Attribute.GCLeafFunction);
}
private LLVMValueRef buildInlineGetRegister(String registerName) {
LLVMTypeRef inlineAsmType = builder.functionType(builder.rawPointerType());
String asmSnippet = LLVMTargetSpecific.get().getRegisterInlineAsm(registerName);
InlineAssemblyConstraint outputConstraint = new InlineAssemblyConstraint(Type.Output, Location.namedRegister(LLVMTargetSpecific.get().getLLVMRegisterName(registerName)));
LLVMValueRef getRegister = builder.buildInlineAsm(inlineAsmType, asmSnippet, false, false, outputConstraint);
LLVMValueRef call = builder.buildCall(getRegister);
builder.setCallSiteAttribute(call, Attribute.GCLeafFunction);
return call;
}
private void setSpecialRegisterValue(SpecialRegister reg, LLVMValueRef value) {
assert getInitialSpecialRegisterValue(reg, currentBlock) != null;
specialRegValues.computeIfAbsent(currentBlock, (b) -> new LLVMValueRef[SpecialRegister.count()])[reg.index] = value;
}
void setInitialSpecialRegisterValue(SpecialRegister reg, LLVMValueRef value) {
initialSpecialRegValues.computeIfAbsent(currentBlock, (b) -> new LLVMValueRef[SpecialRegister.count()])[reg.index] = value;
setSpecialRegisterValue(reg, value);
}
private void setHandlerSpecialRegisterValue(SpecialRegister reg, LLVMValueRef value) {
handlerSpecialRegValues.computeIfAbsent(currentBlock, (b) -> new LLVMValueRef[SpecialRegister.count()])[reg.index] = value;
}
public LLVMValueRef getSpecialRegister(SpecialRegister register) {
if (LLVMOptions.ReturnSpecialRegs.getValue()) {
return getSpecialRegisterValue(register);
}
LLVMValueRef specialRegister;
if (isEntryPoint || canModifySpecialRegisters) {
LLVMValueRef specialRegisterPointer = getSpecialRegisterPointer(register);
specialRegister = builder.buildLoad(specialRegisterPointer, builder.wordType());
} else {
specialRegister = builder.getFunctionParam(register.index);
}
return specialRegister;
}
private LLVMValueRef getSpecialRegisterPointer(SpecialRegister register) {
assert !LLVMOptions.ReturnSpecialRegs.getValue();
if (isEntryPoint) {
return stackSlots[register.index];
} else if (canModifySpecialRegisters) {
return builder.getFunctionParam(register.index);
} else {
throw VMError.shouldNotReachHere();
}
}
private LLVMValueRef getSpecialRegisterArgument(SpecialRegister register, ResolvedJavaMethod targetMethod) {
if (LLVMOptions.ReturnSpecialRegs.getValue()) {
return getSpecialRegisterValue(register);
}
LLVMValueRef specialRegisterArg;
if (targetMethod != null && canModifySpecialRegisters(targetMethod)) {
if (isEntryPoint || canModifySpecialRegisters) {
specialRegisterArg = getSpecialRegisterPointer(register);
} else {
assert GuardedAnnotationAccess.isAnnotationPresent(targetMethod, Uninterruptible.class);
specialRegisterArg = builder.constantNull(builder.pointerType(builder.wordType()));
}
} else if (isEntryPoint || canModifySpecialRegisters) {
specialRegisterArg = builder.buildLoad(getSpecialRegisterPointer(register), builder.wordType());
} else {
specialRegisterArg = getSpecialRegister(register);
}
return specialRegisterArg;
}
LLVMValueRef getSpecialRegisterValue(SpecialRegister reg) {
return getSpecialRegisterValue(reg, currentBlock);
}
LLVMValueRef getSpecialRegisterValue(SpecialRegister reg, Block block) {
return specialRegValues.get(block)[reg.index];
}
LLVMValueRef getInitialSpecialRegisterValue(SpecialRegister reg, Block block) {
if (!initialSpecialRegValues.containsKey(block)) {
return null;
}
return initialSpecialRegValues.get(block)[reg.index];
}
LLVMValueRef getHandlerSpecialRegisterValue(SpecialRegister reg, Block block) {
return handlerSpecialRegValues.get(block)[reg.index];
}
public enum SpecialRegister {
ThreadPointer(SubstrateOptions.MultiThreaded.getValue()),
HeapBase(SubstrateOptions.SpawnIsolates.getValue());
private static final int presentCount;
private static final SpecialRegister[] presentRegisters;
static {
int index = 0;
for (SpecialRegister reg : values()) {
if (reg.isPresent) {
reg.index = index;
index++;
}
}
presentCount = index;
presentRegisters = new SpecialRegister[presentCount];
for (SpecialRegister reg : values()) {
if (reg.isPresent) {
presentRegisters[reg.index] = reg;
}
}
}
private boolean isPresent;
private int index;
SpecialRegister(boolean isPresent) {
this.isPresent = isPresent;
}
int getIndex() {
return index;
}
static boolean hasRegisters() {
return presentCount > 0;
}
static int count() {
return presentCount;
}
static SpecialRegister[] registers() {
return presentRegisters.clone();
}
}
@Override
public LIRGenerationResult getResult() {
throw unimplemented("the LLVM backend doesn't produce an LIRGenerationResult");
}
@Override
public MoveFactory getMoveFactory() {
throw unimplemented("the LLVM backend doesn't use LIR moves");
}
@Override
public MoveFactory getSpillMoveFactory() {
throw unimplemented("the LLVM backend doesn't use LIR moves");
}
@Override
public void emitNullCheck(Value address, LIRFrameState state) {
throw unimplemented("the LLVM backend doesn't support deoptimization");
}
@Override
public void emitDeoptimize(Value actionAndReason, Value failedSpeculation, LIRFrameState state) {
throw unimplemented("the LLVM backend doesn't support deoptimization");
}
@Override
public void emitFarReturn(AllocatableValue result, Value sp, Value ip, boolean fromMethodWithCalleeSavedRegisters) {
throw unimplemented("the LLVM backend delegates exception handling to libunwind");
}
@Override
public void emitUnwind(Value operand) {
throw shouldNotReachHere("handled by lowering");
}
@Override
public void emitVerificationMarker(Object marker) {
}
@Override
public void emitInstructionSynchronizationBarrier() {
throw unimplemented("the LLVM backend doesn't support instruction synchronization");
}
@Override
public <I extends LIRInstruction> I append(I op) {
throw unimplemented("the LLVM backend doesn't support LIR instructions");
}
@Override
public void emitSpeculationFence() {
throw unimplemented("the LLVM backend doesn't support speculative execution attack mitigation");
}
@Override
public LIRInstruction createBenchmarkCounter(String name, String group, Value increment) {
throw unimplemented("the LLVM backend doesn't support diagnostic operations");
}
@Override
public LIRInstruction createMultiBenchmarkCounter(String[] names, String[] groups, Value[] increments) {
throw unimplemented("the LLVM backend doesn't support diagnostic operations");
}
@Override
public StandardOp.ZapRegistersOp createZapRegisters(Register[] zappedRegisters, JavaConstant[] zapValues) {
throw unimplemented("the LLVM backend doesn't support diagnostic operations");
}
@Override
public StandardOp.ZapRegistersOp createZapRegisters(Register[] zappedRegisters) {
throw unimplemented("the LLVM backend doesn't support diagnostic operations");
}
@Override
public StandardOp.ZapRegistersOp createZapRegisters() {
throw unimplemented("the LLVM backend doesn't support diagnostic operations");
}
@Override
public LIRInstruction createZapArgumentSpace(StackSlot[] zappedStack, JavaConstant[] zapValues) {
throw unimplemented("the LLVM backend doesn't support diagnostic operations");
}
@Override
public LIRInstruction zapArgumentSpace() {
throw unimplemented("the LLVM backend doesn't support diagnostic operations");
}
public static class ArithmeticLLVMGenerator implements ArithmeticLIRGeneratorTool, LLVMIntrinsicGenerator {
private final LLVMIRBuilder builder;
ArithmeticLLVMGenerator(LLVMIRBuilder builder) {
this.builder = builder;
}
@Override
public Value emitNegate(Value input) {
LLVMValueRef neg = builder.buildNeg(getVal(input));
return new LLVMVariable(neg);
}
@Override
public Value emitAdd(Value a, Value b, boolean setFlags) {
LLVMValueRef add = builder.buildAdd(getVal(a), getVal(b));
return new LLVMVariable(add);
}
@Override
public Value emitSub(Value a, Value b, boolean setFlags) {
LLVMValueRef sub = builder.buildSub(getVal(a), getVal(b));
return new LLVMVariable(sub);
}
@Override
public Value emitMul(Value a, Value b, boolean setFlags) {
LLVMValueRef mul = builder.buildMul(getVal(a), getVal(b));
return new LLVMVariable(mul);
}
@Override
public Value emitMulHigh(Value a, Value b) {
return emitMulHigh(a, b, true);
}
@Override
public Value emitUMulHigh(Value a, Value b) {
return emitMulHigh(a, b, false);
}
private LLVMVariable emitMulHigh(Value a, Value b, boolean signed) {
LLVMValueRef valA = getVal(a);
LLVMValueRef valB = getVal(b);
assert LLVMIRBuilder.compatibleTypes(typeOf(valA), typeOf(valB)) : dumpValues("invalid mulhigh arguments", valA, valB);
int baseBits = LLVMIRBuilder.integerTypeWidth(LLVMIRBuilder.typeOf(valA));
int extendedBits = baseBits * 2;
BiFunction<LLVMValueRef, Integer, LLVMValueRef> extend = (signed) ? builder::buildSExt : builder::buildZExt;
valA = extend.apply(valA, extendedBits);
valB = extend.apply(valB, extendedBits);
LLVMValueRef mul = builder.buildMul(valA, valB);
BiFunction<LLVMValueRef, LLVMValueRef, LLVMValueRef> shift = (signed) ? builder::buildShr : builder::buildUShr;
LLVMValueRef shiftedMul = shift.apply(mul, builder.constantInteger(baseBits, extendedBits));
LLVMValueRef truncatedMul = builder.buildTrunc(shiftedMul, baseBits);
return new LLVMVariable(truncatedMul);
}
@Override
public Value emitDiv(Value a, Value b, LIRFrameState state) {
LLVMValueRef div = builder.buildDiv(getVal(a), getVal(b));
return new LLVMVariable(div);
}
@Override
public Value emitRem(Value a, Value b, LIRFrameState state) {
LLVMValueRef rem = builder.buildRem(getVal(a), getVal(b));
return new LLVMVariable(rem);
}
@Override
public Value emitUDiv(Value a, Value b, LIRFrameState state) {
LLVMValueRef uDiv = builder.buildUDiv(getVal(a), getVal(b));
return new LLVMVariable(uDiv);
}
@Override
public Value emitURem(Value a, Value b, LIRFrameState state) {
LLVMValueRef uRem = builder.buildURem(getVal(a), getVal(b));
return new LLVMVariable(uRem);
}
@Override
public Value emitNot(Value input) {
LLVMValueRef not = builder.buildNot(getVal(input));
return new LLVMVariable(not);
}
@Override
public Value emitAnd(Value a, Value b) {
LLVMValueRef and = builder.buildAnd(getVal(a), getVal(b));
return new LLVMVariable(and);
}
@Override
public Value emitOr(Value a, Value b) {
LLVMValueRef or = builder.buildOr(getVal(a), getVal(b));
return new LLVMVariable(or);
}
@Override
public Value emitXor(Value a, Value b) {
LLVMValueRef xor = builder.buildXor(getVal(a), getVal(b));
return new LLVMVariable(xor);
}
@Override
public Value emitShl(Value a, Value b) {
LLVMValueRef valA = getVal(a);
LLVMValueRef shl = builder.buildShl(valA, emitIntegerConvert(getVal(b), typeOf(valA)));
return new LLVMVariable(shl);
}
@Override
public Value emitShr(Value a, Value b) {
LLVMValueRef valA = getVal(a);
LLVMValueRef shr = builder.buildShr(valA, emitIntegerConvert(getVal(b), typeOf(valA)));
return new LLVMVariable(shr);
}
@Override
public Value emitUShr(Value a, Value b) {
LLVMValueRef valA = getVal(a);
LLVMValueRef ushr = builder.buildUShr(valA, emitIntegerConvert(getVal(b), typeOf(valA)));
return new LLVMVariable(ushr);
}
private LLVMValueRef emitIntegerConvert(LLVMValueRef value, LLVMTypeRef type) {
int fromBits = LLVMIRBuilder.integerTypeWidth(typeOf(value));
int toBits = LLVMIRBuilder.integerTypeWidth(type);
if (fromBits < toBits) {
return (fromBits == 1) ? builder.buildZExt(value, toBits) : builder.buildSExt(value, toBits);
}
if (fromBits > toBits) {
return builder.buildTrunc(value, toBits);
}
return value;
}
@Override
public Value emitFloatConvert(FloatConvert op, Value inputVal) {
LLVMTypeRef destType;
switch (op) {
case F2I:
case D2I:
destType = builder.intType();
break;
case F2L:
case D2L:
destType = builder.longType();
break;
case I2F:
case L2F:
case D2F:
destType = builder.floatType();
break;
case I2D:
case L2D:
case F2D:
destType = builder.doubleType();
break;
default:
throw shouldNotReachHere("invalid FloatConvert type");
}
LLVMValueRef convert;
switch (op.getCategory()) {
case FloatingPointToInteger:
LLVMValueRef value = getVal(inputVal);
LLVMValueRef isNan = builder.buildCompare(Condition.NE, value, value, true);
LLVMValueRef converted = builder.buildFPToSI(getVal(inputVal), destType);
LLVMValueRef zero = builder.constantInteger(0, LLVMIRBuilder.integerTypeWidth(destType));
convert = builder.buildSelect(isNan, zero, converted);
break;
case IntegerToFloatingPoint:
convert = builder.buildSIToFP(getVal(inputVal), destType);
break;
case FloatingPointToFloatingPoint:
convert = builder.buildFPCast(getVal(inputVal), destType);
break;
default:
throw shouldNotReachHere("invalid FloatConvert type");
}
return new LLVMVariable(convert);
}
@Override
public Value emitReinterpret(LIRKind to, Value inputVal) {
LLVMTypeRef type = getType(to);
LLVMValueRef cast = builder.buildBitcast(getVal(inputVal), type);
return new LLVMVariable(cast);
}
@Override
public Value emitNarrow(Value inputVal, int bits) {
LLVMValueRef narrow = builder.buildTrunc(getVal(inputVal), bits);
return new LLVMVariable(narrow);
}
@Override
public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
LLVMValueRef signExtend = builder.buildSExt(getVal(inputVal), toBits);
return new LLVMVariable(signExtend);
}
@Override
public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
LLVMValueRef zeroExtend = builder.buildZExt(getVal(inputVal), toBits);
return new LLVMVariable(zeroExtend);
}
@Override
public Value emitMathAbs(Value input) {
LLVMValueRef abs = builder.buildAbs(getVal(input));
return new LLVMVariable(abs);
}
@Override
public Value emitMathSqrt(Value input) {
LLVMValueRef sqrt = builder.buildSqrt(getVal(input));
return new LLVMVariable(sqrt);
}
@Override
public Value emitMathLog(Value input, boolean base10) {
LLVMValueRef value = getVal(input);
LLVMValueRef log = base10 ? builder.buildLog10(value) : builder.buildLog(value);
return new LLVMVariable(log);
}
@Override
public Value emitMathCos(Value input) {
LLVMValueRef cos = builder.buildCos(getVal(input));
return new LLVMVariable(cos);
}
@Override
public Value emitMathSin(Value input) {
LLVMValueRef sin = builder.buildSin(getVal(input));
return new LLVMVariable(sin);
}
@Override
public Value emitMathTan(Value input) {
LLVMValueRef value = getVal(input);
LLVMValueRef sin = builder.buildSin(value);
LLVMValueRef cos = builder.buildCos(value);
LLVMValueRef tan = builder.buildDiv(sin, cos);
return new LLVMVariable(tan);
}
@Override
public Value emitMathExp(Value input) {
LLVMValueRef exp = builder.buildExp(getVal(input));
return new LLVMVariable(exp);
}
@Override
public Value emitMathPow(Value x, Value y) {
LLVMValueRef pow = builder.buildPow(getVal(x), getVal(y));
return new LLVMVariable(pow);
}
@Override
public Value emitMathCeil(Value input) {
LLVMValueRef ceil = builder.buildCeil(getVal(input));
return new LLVMVariable(ceil);
}
@Override
public Value emitMathFloor(Value input) {
LLVMValueRef floor = builder.buildFloor(getVal(input));
return new LLVMVariable(floor);
}
@Override
public Value emitCountLeadingZeros(Value input) {
LLVMValueRef ctlz = builder.buildCtlz(getVal(input));
ctlz = emitIntegerConvert(ctlz, builder.intType());
return new LLVMVariable(ctlz);
}
@Override
public Value emitCountTrailingZeros(Value input) {
LLVMValueRef cttz = builder.buildCttz(getVal(input));
cttz = emitIntegerConvert(cttz, builder.intType());
return new LLVMVariable(cttz);
}
@Override
public Value emitBitCount(Value operand) {
LLVMValueRef op = getVal(operand);
LLVMValueRef answer = builder.buildCtpop(op);
answer = emitIntegerConvert(answer, builder.intType());
return new LLVMVariable(answer);
}
@Override
public Value emitBitScanForward(Value operand) {
LLVMValueRef op = getVal(operand);
LLVMValueRef trailingZeros = builder.buildCttz(op);
int resultSize = LLVMIRBuilder.integerTypeWidth(typeOf(trailingZeros));
int expectedSize = JavaKind.Int.getBitCount();
if (resultSize < expectedSize) {
trailingZeros = builder.buildZExt(trailingZeros, expectedSize);
} else if (resultSize > expectedSize) {
trailingZeros = builder.buildTrunc(trailingZeros, expectedSize);
}
return new LLVMVariable(trailingZeros);
}
@Override
public Value emitBitScanReverse(Value operand) {
LLVMValueRef op = getVal(operand);
int opSize = LLVMIRBuilder.integerTypeWidth(typeOf(op));
int expectedSize = JavaKind.Int.getBitCount();
LLVMValueRef leadingZeros = builder.buildCtlz(op);
if (opSize < expectedSize) {
leadingZeros = builder.buildZExt(leadingZeros, expectedSize);
} else if (opSize > expectedSize) {
leadingZeros = builder.buildTrunc(leadingZeros, expectedSize);
}
LLVMValueRef result = builder.buildSub(builder.constantInt(opSize - 1), leadingZeros);
return new LLVMVariable(result);
}
@Override
public Value emitFusedMultiplyAdd(Value a, Value b, Value c) {
LLVMValueRef fma = builder.buildFma(getVal(a), getVal(b), getVal(c));
return new LLVMVariable(fma);
}
@Override
public Value emitMathMin(Value a, Value b) {
LLVMValueRef min = builder.buildMin(getVal(a), getVal(b));
return new LLVMVariable(min);
}
@Override
public Value emitMathMax(Value a, Value b) {
LLVMValueRef max = builder.buildMax(getVal(a), getVal(b));
return new LLVMVariable(max);
}
@Override
public Value emitMathCopySign(Value a, Value b) {
LLVMValueRef copySign = builder.buildCopysign(getVal(a), getVal(b));
return new LLVMVariable(copySign);
}
@Override
public Variable emitLoad(LIRKind kind, Value address, LIRFrameState state) {
LLVMValueRef load = builder.buildLoad(getVal(address), getType(kind));
return new LLVMVariable(load);
}
@Override
public void emitStore(ValueKind<?> kind, Value addr, Value input, LIRFrameState state) {
LLVMValueRef address = getVal(addr);
LLVMValueRef value = getVal(input);
LLVMTypeRef addressType = LLVMIRBuilder.typeOf(address);
LLVMTypeRef valueType = LLVMIRBuilder.typeOf(value);
LLVMValueRef castedValue = value;
if (LLVMIRBuilder.isObjectType(valueType) && !LLVMIRBuilder.isObjectType(addressType)) {
valueType = builder.rawPointerType();
castedValue = builder.buildAddrSpaceCast(value, builder.rawPointerType());
}
LLVMValueRef castedAddress = builder.buildBitcast(address, builder.pointerType(valueType, LLVMIRBuilder.isObjectType(addressType), false));
builder.buildStore(castedValue, castedAddress);
}
@Override
public Variable emitVolatileLoad(LIRKind kind, Value address, LIRFrameState state) {
throw GraalError.shouldNotReachHere();
}
@Override
public void emitVolatileStore(ValueKind<?> kind, Value address, Value input, LIRFrameState state) {
throw GraalError.shouldNotReachHere();
}
}
static class DebugInfoPrinter {
private LLVMGenerator gen;
private LLVMIRBuilder builder;
private int debugLevel;
private LLVMValueRef indentCounter;
private LLVMValueRef spacesVector;
DebugInfoPrinter(LLVMGenerator gen, int debugLevel) {
this.gen = gen;
this.builder = gen.getBuilder();
this.debugLevel = debugLevel;
if (debugLevel >= DebugLevel.Function.level) {
this.indentCounter = builder.getUniqueGlobal("__svm_indent_counter", builder.intType(), true);
this.spacesVector = builder.getUniqueGlobal("__svm_spaces_vector", builder.vectorType(builder.rawPointerType(), 100), false);
StringBuilder strBuilder = new StringBuilder();
LLVMValueRef[] strings = new LLVMValueRef[100];
for (int i = 0; i < 100; ++i) {
strings[i] = builder.getUniqueGlobal("__svm_" + i + "_spaces", builder.arrayType(builder.byteType(), strBuilder.length() + 1), false);
builder.setInitializer(strings[i], builder.constantString(strBuilder.toString()));
strings[i] = builder.buildBitcast(strings[i], builder.rawPointerType());
strBuilder.append(' ');
}
builder.setInitializer(spacesVector, builder.constantVector(strings));
}
}
void printFunction(StructuredGraph graph, NodeLLVMBuilder nodeBuilder) {
if (debugLevel >= DebugLevel.Function.level) {
indent();
List<JavaKind> printfTypes = new ArrayList<>();
List<LLVMValueRef> printfArgs = new ArrayList<>();
for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
printfTypes.add(param.getStackKind());
printfArgs.add(getVal(nodeBuilder.operand(param)));
}
String functionName = gen.getFunctionName();
emitPrintf("In " + functionName, printfTypes.toArray(new JavaKind[0]), printfArgs.toArray(new LLVMValueRef[0]));
}
}
void printBlock(Block block) {
if (debugLevel >= DebugLevel.Block.level) {
emitPrintf("In block " + block.toString());
}
}
void printNode(ValueNode valueNode) {
if (debugLevel >= DebugLevel.Node.level) {
emitPrintf(valueNode.toString());
}
}
void printIndirectCall(ResolvedJavaMethod targetMethod, LLVMValueRef callee) {
if (debugLevel >= DebugLevel.Node.level) {
emitPrintf("Indirect call to " + ((targetMethod != null) ? targetMethod.getName() : "[unknown]"), new JavaKind[]{JavaKind.Object}, new LLVMValueRef[]{callee});
}
}
void printBreakpoint() {
if (debugLevel >= DebugLevel.Function.level) {
emitPrintf("breakpoint");
}
}
void printRetVoid() {
if (debugLevel >= DebugLevel.Function.level) {
emitPrintf("Return");
deindent();
}
}
void printRet(JavaKind kind, Value input) {
if (debugLevel >= DebugLevel.Function.level) {
emitPrintf("Return", new JavaKind[]{kind}, new LLVMValueRef[]{getVal(input)});
deindent();
}
}
void setValueName(LLVMValueWrapper value, ValueNode node) {
if (debugLevel >= DebugLevel.Node.level && node.getStackKind() != JavaKind.Void) {
builder.setValueName(value.get(), node.toString());
}
}
void indent() {
LLVMValueRef counter = builder.buildLoad(indentCounter);
LLVMValueRef newCounter = builder.buildAdd(counter, builder.constantInt(1));
builder.buildStore(newCounter, indentCounter);
}
private void deindent() {
LLVMValueRef counter = builder.buildLoad(indentCounter);
LLVMValueRef newCounter = builder.buildSub(counter, builder.constantInt(1));
builder.buildStore(newCounter, indentCounter);
}
private void emitPrintf(String base) {
emitPrintf(base, new JavaKind[0], new LLVMValueRef[0]);
}
private void emitPrintf(String base, JavaKind[] types, LLVMValueRef[] values) {
LLVMValueRef printf = builder.getFunction("printf", builder.functionType(builder.intType(), true, builder.rawPointerType()));
if (debugLevel >= DebugLevel.Function.level) {
LLVMValueRef count = builder.buildLoad(indentCounter);
LLVMValueRef vector = builder.buildLoad(spacesVector);
LLVMValueRef spaces = builder.buildExtractElement(vector, count);
builder.buildCall(printf, spaces);
}
StringBuilder introString = new StringBuilder(base);
List<LLVMValueRef> printfArgs = new ArrayList<>();
assert types.length == values.length;
for (int i = 0; i < types.length; ++i) {
switch (types[i]) {
case Boolean:
case Byte:
introString.append(" %hhd ");
break;
case Short:
introString.append(" %hd ");
break;
case Char:
introString.append(" %c ");
break;
case Int:
introString.append(" %ld ");
break;
case Float:
case Double:
introString.append(" %f ");
break;
case Long:
introString.append(" %lld ");
break;
case Object:
introString.append(" %p ");
break;
case Void:
case Illegal:
default:
throw shouldNotReachHere();
}
printfArgs.add(values[i]);
}
introString.append("\n");
printfArgs.add(0, builder.buildGlobalStringPtr(introString.toString()));
builder.buildCall(printf, printfArgs.toArray(new LLVMValueRef[0]));
}
public enum DebugLevel {
Function(1),
Block(2),
Node(3);
private int level;
DebugLevel(int level) {
this.level = level;
}
}
}
}