package com.oracle.truffle.llvm.runtime.datalayout;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import com.oracle.truffle.llvm.runtime.datalayout.DataLayoutParser.DataTypeSpecification;
import com.oracle.truffle.llvm.runtime.types.FunctionType;
import com.oracle.truffle.llvm.runtime.types.PointerType;
import com.oracle.truffle.llvm.runtime.types.PrimitiveType;
import com.oracle.truffle.llvm.runtime.types.Type;
import com.oracle.truffle.llvm.runtime.types.Type.TypeOverflowException;
import com.oracle.truffle.llvm.runtime.types.VariableBitWidthType;
public final class DataLayout {
private final ArrayList<DataTypeSpecification> dataLayout;
private final ByteOrder byteOrder;
private final IdentityHashMap<Type, Long> sizeCache = new IdentityHashMap<>();
private final IdentityHashMap<Type, Integer> alignmentCache = new IdentityHashMap<>();
public DataLayout(ByteOrder byteOrder) {
this.dataLayout = new ArrayList<>();
this.byteOrder = byteOrder;
}
public DataLayout(String layout) {
this.dataLayout = new ArrayList<>();
this.byteOrder = DataLayoutParser.parseDataLayout(layout, dataLayout);
}
public ByteOrder getByteOrder() {
return byteOrder;
}
public long getSize(Type type) throws TypeOverflowException {
Long cachedSize = sizeCache.get(type);
if (cachedSize != null) {
return cachedSize;
}
long size = type.getBitSize();
int align = getBitAlignment(type);
long rem = Long.remainderUnsigned(size, align);
if (rem != 0) {
size = Type.addUnsignedExact(size, Type.subUnsignedExact(align, rem));
}
size = Math.max(1, Long.divideUnsigned(size, Byte.SIZE));
sizeCache.put(type, size);
return size;
}
public int getBitAlignment(Type baseType) {
Integer cachedAlignment = alignmentCache.get(baseType);
if (cachedAlignment != null) {
return cachedAlignment;
}
DataTypeSpecification spec = getDataTypeSpecification(baseType);
if (spec == null) {
throw new IllegalStateException("No data specification found for " + baseType + ". Data layout is " + dataLayout);
}
int alignment = spec.getAbiAlignment();
alignmentCache.put(baseType, alignment);
return alignment;
}
public DataLayout merge(DataLayout other) {
if (other.byteOrder != byteOrder) {
throw new IllegalStateException("Multiple bitcode files with incompatible byte order are used: " + this.toString() + " vs. " + other.toString());
}
DataLayout result = new DataLayout(byteOrder);
for (DataTypeSpecification otherEntry : other.dataLayout) {
DataTypeSpecification thisEntry;
if (otherEntry.getType() == DataLayoutType.POINTER || otherEntry.getType() == DataLayoutType.INTEGER_WIDTHS) {
thisEntry = getDataTypeSpecification(otherEntry.getType());
} else if (otherEntry.getType() == DataLayoutType.INTEGER || otherEntry.getType() == DataLayoutType.FLOAT) {
thisEntry = getDataTypeSpecification(otherEntry.getType(), otherEntry.getSize());
} else {
throw new IllegalStateException("Unknown data layout type: " + otherEntry.getType());
}
result.dataLayout.add(otherEntry);
if (thisEntry != null && !thisEntry.equals(otherEntry)) {
throw new IllegalStateException("Multiple bitcode files with incompatible layout strings are used: " + this.toString() + " vs. " + other.toString());
}
}
return result;
}
@Override
public String toString() {
return dataLayout.toString();
}
private DataTypeSpecification getDataTypeSpecification(Type baseType) {
if (baseType instanceof PointerType || baseType instanceof FunctionType) {
DataTypeSpecification ptrDTSpec = getDataTypeSpecification(DataLayoutType.POINTER, 64);
return ptrDTSpec == null ? getDataTypeSpecification(DataLayoutType.POINTER) : ptrDTSpec;
} else if (baseType instanceof PrimitiveType) {
PrimitiveType primitiveType = (PrimitiveType) baseType;
switch (primitiveType.getPrimitiveKind()) {
case I1:
case I8:
return getDataTypeSpecification(DataLayoutType.INTEGER, 8);
case I16:
return getDataTypeSpecification(DataLayoutType.INTEGER, 16);
case I32:
return getDataTypeSpecification(DataLayoutType.INTEGER, 32);
case I64:
return getDataTypeSpecification(DataLayoutType.INTEGER, 64);
case HALF:
return getDataTypeSpecification(DataLayoutType.FLOAT, 16);
case FLOAT:
return getDataTypeSpecification(DataLayoutType.FLOAT, 32);
case DOUBLE:
return getDataTypeSpecification(DataLayoutType.FLOAT, 64);
case X86_FP80:
return getDataTypeSpecification(DataLayoutType.FLOAT, 80);
}
} else if (baseType instanceof VariableBitWidthType) {
int bits = ((VariableBitWidthType) baseType).getBitSizeInt();
DataTypeSpecification largest = null;
DataTypeSpecification smallestLarger = null;
for (DataTypeSpecification spec : dataLayout) {
if (spec.getType() == DataLayoutType.INTEGER) {
if (largest == null || largest.getSize() < spec.getSize()) {
largest = spec;
}
if (spec.getSize() >= bits) {
if (smallestLarger == null || smallestLarger.getSize() > spec.getSize()) {
smallestLarger = spec;
}
}
}
}
if (smallestLarger != null) {
return smallestLarger;
} else {
return largest;
}
}
return null;
}
private DataTypeSpecification getDataTypeSpecification(DataLayoutType dataLayoutType) {
for (DataTypeSpecification spec : dataLayout) {
if (spec.getType().equals(dataLayoutType)) {
return spec;
}
}
return null;
}
private DataTypeSpecification getDataTypeSpecification(DataLayoutType dataLayoutType, int size) {
for (DataTypeSpecification spec : dataLayout) {
if (spec.getType().equals(dataLayoutType) && size == spec.getSize()) {
return spec;
}
}
return null;
}
}