org.qbicc.plugin.llvm.LLVMModuleNodeVisitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qbicc-plugin-llvm Show documentation
Show all versions of qbicc-plugin-llvm Show documentation
Support for the LLVM backend
package org.qbicc.plugin.llvm;
import static org.qbicc.machine.llvm.Types.array;
import static org.qbicc.machine.llvm.Types.*;
import static org.qbicc.machine.llvm.Values.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.smallrye.common.constraint.Assert;
import org.qbicc.context.CompilationContext;
import org.qbicc.context.Location;
import org.qbicc.graph.Value;
import org.qbicc.graph.literal.ArrayLiteral;
import org.qbicc.graph.literal.BitCastLiteral;
import org.qbicc.graph.literal.BooleanLiteral;
import org.qbicc.graph.literal.ByteArrayLiteral;
import org.qbicc.graph.literal.CompoundLiteral;
import org.qbicc.graph.literal.ElementOfLiteral;
import org.qbicc.graph.literal.FloatLiteral;
import org.qbicc.graph.literal.FunctionLiteral;
import org.qbicc.graph.literal.GlobalVariableLiteral;
import org.qbicc.graph.literal.IntegerLiteral;
import org.qbicc.graph.literal.Literal;
import org.qbicc.graph.literal.LiteralVisitor;
import org.qbicc.graph.literal.MemberOfLiteral;
import org.qbicc.graph.literal.NullLiteral;
import org.qbicc.graph.literal.OffsetFromLiteral;
import org.qbicc.graph.literal.ProgramObjectLiteral;
import org.qbicc.graph.literal.TypeLiteral;
import org.qbicc.graph.literal.UndefinedLiteral;
import org.qbicc.graph.literal.ValueConvertLiteral;
import org.qbicc.graph.literal.ZeroInitializerLiteral;
import org.qbicc.machine.llvm.Array;
import org.qbicc.machine.llvm.Function;
import org.qbicc.machine.llvm.FunctionAttributes;
import org.qbicc.machine.llvm.IdentifiedType;
import org.qbicc.machine.llvm.LLValue;
import org.qbicc.machine.llvm.Module;
import org.qbicc.machine.llvm.ParameterAttributes;
import org.qbicc.machine.llvm.Struct;
import org.qbicc.machine.llvm.StructType;
import org.qbicc.machine.llvm.Types;
import org.qbicc.machine.llvm.Values;
import org.qbicc.machine.llvm.impl.LLVM;
import org.qbicc.plugin.coreclasses.CoreClasses;
import org.qbicc.type.ArrayObjectType;
import org.qbicc.type.ArrayType;
import org.qbicc.type.BlockType;
import org.qbicc.type.BooleanType;
import org.qbicc.type.CompoundType;
import org.qbicc.type.FloatType;
import org.qbicc.type.FunctionType;
import org.qbicc.type.IntegerType;
import org.qbicc.type.InvokableType;
import org.qbicc.type.ObjectType;
import org.qbicc.type.PointerType;
import org.qbicc.type.ReferenceType;
import org.qbicc.type.Type;
import org.qbicc.type.UnionType;
import org.qbicc.type.UnresolvedType;
import org.qbicc.type.ValueType;
import org.qbicc.type.VariadicType;
import org.qbicc.type.VoidType;
import org.qbicc.type.WordType;
final class LLVMModuleNodeVisitor implements LiteralVisitor {
static final LLValue i8_ptr = ptrTo(i8);
static final LLValue i8_ptr_as1 = ptrTo(i8, 1);
static final LLValue ptr_as1 = ptr(1);
final LLVMModuleGenerator generator;
final Module module;
final CompilationContext ctxt;
final LLVMConfiguration config;
final boolean opaquePointers;
final Map types = new HashMap<>();
final Map> structureOffsets = new HashMap<>();
final Map globalValues = new HashMap<>();
final Map statepointDecls = new HashMap<>();
final Map statepointDeclsByName = new HashMap<>();
final Map statepointTypes = new HashMap<>();
final Map resultDecls = new HashMap<>();
final Map resultDeclsByName = new HashMap<>();
final Map resultDeclTypes = new HashMap<>();
final LLValue refType;
final LLValue relocateDeclType;
LLValue relocateDecl;
LLVMModuleNodeVisitor(final LLVMModuleGenerator generator, final Module module, final CompilationContext ctxt, final LLVMConfiguration config) {
this.generator = generator;
this.module = module;
this.ctxt = ctxt;
this.config = config;
this.opaquePointers = config.isOpaquePointers();
// References can be used as different types in the IL without manually casting them, so we need to
// represent all reference types as being the same LLVM type. We will cast to and from the actual type we
// use the reference as when needed.
this.refType = module.identifiedType("ref").type(switch (config.getReferenceStrategy()) {
case POINTER -> opaquePointers ? ptr : i8_ptr;
case POINTER_AS1 -> opaquePointers ? ptr_as1 : i8_ptr_as1;
}).asTypeRef();
relocateDeclType = function(refType, List.of(token, i32, i32), false);
}
LLValue map(Type type) {
LLValue res = types.get(type);
if (res != null) {
return res;
}
if (type instanceof VoidType) {
res = void_;
} else if (type instanceof FunctionType) {
FunctionType fnType = (FunctionType) type;
int cnt = fnType.getParameterCount();
List argTypes = cnt == 0 ? List.of() : new ArrayList<>(cnt);
boolean variadic = false;
for (int i = 0; i < cnt; i ++) {
ValueType parameterType = fnType.getParameterType(i);
if (parameterType instanceof VariadicType) {
if (i < cnt - 1) {
throw new IllegalStateException("Variadic type as non-final parameter type");
}
variadic = true;
} else {
argTypes.add(map(parameterType));
}
}
res = Types.function(map(fnType.getReturnType()), argTypes, variadic);
} else if (type instanceof BooleanType) {
// todo: sometimes it's one byte instead
res = i1;
} else if (type instanceof FloatType) {
int bytes = (int) ((FloatType) type).getSize();
if (bytes == 4) {
res = float32;
} else if (bytes == 8) {
res = float64;
} else {
throw Assert.unreachableCode();
}
} else if (type instanceof InvokableType it) {
// LLVM does not have an equivalent to method types
res = map(ctxt.getFunctionTypeForInvokableType(it));
} else if (type instanceof PointerType) {
Type pointeeType = ((PointerType) type).getPointeeType();
res = opaquePointers ? ptr : pointeeType instanceof VoidType ? i8_ptr : ptrTo(map(pointeeType));
} else if (type instanceof ReferenceType || type instanceof UnresolvedType) {
res = refType;
} else if (type instanceof WordType) {
// all other words are integers
// LLVM doesn't really care about signedness
int bytes = (int) ((WordType) type).getSize();
if (bytes == 1) {
res = i8;
} else if (bytes == 2) {
res = i16;
} else if (bytes == 4) {
res = i32;
} else if (bytes == 8) {
res = i64;
} else {
throw Assert.unreachableCode();
}
} else if (type instanceof ArrayType) {
ArrayType arrayType = (ArrayType) type;
Type elementType = arrayType.getElementType();
long size = arrayType.getElementCount();
res = array((int) size, map(elementType));
} else if (type instanceof CompoundType) {
// Compound types are special in that they can be self-referential by containing pointers to themselves. To
// handle this, we must do two special things:
// - Use an identified type in the module to avoid infinite recursion when printing the type
// - Add the mapping to types early to avoid infinite recursion when mapping self-referential member types
CompoundType compoundType = (CompoundType) type;
HashMap offsets = new HashMap<>();
boolean isIdentified = !compoundType.isAnonymous();
structureOffsets.putIfAbsent(compoundType, offsets);
IdentifiedType identifiedType = null;
if (isIdentified) {
String compoundName = compoundType.getName();
String name;
if (compoundType.getTag() == CompoundType.Tag.NONE) {
String outputName = "type." + compoundName;
name = LLVM.needsQuotes(compoundName) ? LLVM.quoteString(outputName) : outputName;
} else {
String outputName = compoundType.getTag() + "." + compoundName;
name = LLVM.needsQuotes(compoundName) ? LLVM.quoteString(outputName) : outputName;
}
identifiedType = module.identifiedType(name);
types.put(type, identifiedType.asTypeRef());
}
StructType struct = structType(isIdentified);
int index = 0;
for (CompoundType.Member member : compoundType.getPaddedMembers()) {
ValueType memberType = member.getType();
struct.member(map(memberType), member.getName());
// todo: cache these ints
offsets.put(member, Values.intConstant(index));
index ++;
// the target will already pad out for normal alignment
}
if (isIdentified) {
identifiedType.type(struct);
res = identifiedType.asTypeRef();
} else {
res = struct;
}
} else if (type instanceof UnionType ut) {
// Unions are not directly supported but pointers can be cast
res = array((int) ut.getSize(), i8);
} else if (type instanceof BlockType) {
res = label;
} else {
throw new IllegalStateException("Can't map Type("+ type.toString() + ")");
}
types.put(type, res);
return res;
}
LLValue map(final CompoundType compoundType, final CompoundType.Member member) {
// populate map
map(compoundType);
return structureOffsets.get(compoundType).get(member);
}
LLValue map(final Literal value) {
LLValue mapped = globalValues.get(value);
if (mapped != null) {
return mapped;
}
mapped = value.accept(this, null);
globalValues.put(value, mapped);
return mapped;
}
public LLValue visit(final Void param, final ArrayLiteral node) {
List values = node.getValues();
Array array = Values.array(map(node.getType().getElementType()));
for (int i = 0; i < values.size(); i++) {
array.item(map(values.get(i)));
}
return array;
}
public LLValue visit(final Void param, final BitCastLiteral node) {
LLValue input = map(node.getValue());
final ValueType inputType = node.getValue().getType();
final WordType outputType = node.getType();
if (config.isOpaquePointers()) {
if (inputType instanceof PointerType && outputType instanceof PointerType || inputType instanceof ReferenceType && outputType instanceof ReferenceType) {
return input;
}
}
LLValue fromType = map(inputType);
LLValue toType = map(outputType);
if (fromType.equals(toType)) {
return input;
}
return Values.bitcastConstant(input, fromType, toType);
}
public LLValue visit(final Void param, final ValueConvertLiteral node) {
LLValue input = map(node.getValue());
ValueType inputType = node.getValue().getType();
LLValue fromType = map(inputType);
WordType outputType = node.getType();
LLValue toType = map(outputType);
if (fromType.equals(toType)) {
return input;
}
if (inputType instanceof IntegerType && outputType instanceof PointerType) {
return Values.inttoptrConstant(input, fromType, toType);
} else if (inputType instanceof PointerType && outputType instanceof IntegerType) {
return Values.ptrtointConstant(input, fromType, toType);
} else if (inputType instanceof ReferenceType && outputType instanceof PointerType) {
return switch (config.getReferenceStrategy()) {
case POINTER -> input;
case POINTER_AS1 -> Values.addrspacecastConstant(input, fromType, toType);
};
} else if (inputType instanceof PointerType && outputType instanceof ReferenceType) {
return switch (config.getReferenceStrategy()) {
case POINTER -> input;
case POINTER_AS1 -> Values.addrspacecastConstant(input, fromType, toType);
};
}
// todo: add signed/unsigned int <-> fp
return visitAny(param, node);
}
public LLValue visit(final Void param, final ByteArrayLiteral node) {
return Values.byteArray(node.getValues());
}
public LLValue visit(final Void param, final CompoundLiteral node) {
CompoundType type = node.getType();
Map values = node.getValues();
// very similar to emitting a struct type, but we don't have to cache the structure offsets
Struct struct = struct();
for (CompoundType.Member member : type.getPaddedMembers()) {
Literal literal = values.get(member);
ValueType memberType = member.getType();
if (literal == null) {
// no value for this member
struct.item(map(memberType), zeroinitializer);
} else {
struct.item(map(memberType), map(literal));
}
}
return struct;
}
public LLValue visit(final Void param, final ElementOfLiteral node) {
final Literal innermost = innermostGepValue(node);
final PointerType ptrType = innermost.getType(PointerType.class);
return Values.gepConstant(map(ptrType.getPointeeType()), map(ptrType), map(innermost), buildGepLiteralArgs(node, 0));
}
public LLValue visit(final Void param, final FloatLiteral node) {
if (node.getType().getMinBits() == 32) {
return Values.floatConstant(node.floatValue());
} else { // Should be 64
return Values.floatConstant(node.doubleValue());
}
}
public LLValue visit(Void unused, FunctionLiteral literal) {
return Values.global(literal.getExecutable().getName());
}
public LLValue visit(final Void unused, final GlobalVariableLiteral node) {
return Values.global(node.getVariableElement().getName());
}
public LLValue visit(final Void param, final IntegerLiteral node) {
return Values.intConstant(node.longValue());
}
public LLValue visit(final Void unused, final MemberOfLiteral node) {
final Literal innermost = innermostGepValue(node);
final PointerType ptrType = innermost.getType(PointerType.class);
return Values.gepConstant(map(ptrType.getPointeeType()), map(ptrType), map(innermost), buildGepLiteralArgs(node, 0));
}
public LLValue visit(final Void param, final NullLiteral node) {
return NULL;
}
public LLValue visit(final Void unused, final OffsetFromLiteral node) {
final Literal innermost = innermostGepValue(node);
final PointerType ptrType = innermost.getType(PointerType.class);
return Values.gepConstant(map(ptrType.getPointeeType()), map(ptrType), map(innermost), buildGepLiteralArgs(node, 0));
}
public LLValue visit(final Void param, final ProgramObjectLiteral node) {
// todo: auto-declare goes here
return Values.global(node.getProgramObject().getName());
}
public LLValue visit(final Void param, final ZeroInitializerLiteral node) {
return Values.zeroinitializer;
}
public LLValue visit(final Void param, final BooleanLiteral node) {
return node.booleanValue() ? TRUE : FALSE;
}
public LLValue visit(Void param, UndefinedLiteral node) {
return Values.UNDEF;
}
public LLValue visit(Void param, TypeLiteral node) {
ValueType type = node.getValue();
// common cases first
int typeId;
if (type instanceof ArrayObjectType) {
typeId = CoreClasses.get(ctxt).getArrayContentField((ArrayObjectType) type).getEnclosingType().load().getTypeId();
} else if (type instanceof ObjectType) {
typeId = ((ObjectType) type).getDefinition().load().getTypeId();
} else if (type instanceof WordType) {
typeId = ((WordType) type).asPrimitive().getTypeId();
} else if (type instanceof VoidType) {
typeId = ((VoidType) type).asPrimitive().getTypeId();
} else {
// not a valid type literal
ctxt.error("llvm: cannot lower type literal %s", node);
return Values.intConstant(0);
}
if (typeId <= 0) {
ctxt.error("llvm: type %s has invalid type ID %d", type, typeId);
}
return Values.intConstant(typeId);
}
@Override
public LLValue visitAny(Void unused, Literal literal) {
ctxt.error(Location.builder().setNode(literal).build(), "llvm: Unrecognized literal type %s", literal.getClass());
return LLVM.FALSE;
}
private LLValue[] buildGepLiteralArgs(Literal lit, int index) {
if (lit instanceof ElementOfLiteral eol) {
LLValue[] array = buildGepLiteralArgs(eol.getArrayPointer(), index + 2);
final int length = array.length;
array[length - index - 2] = map(eol.getIndex().getType());
array[length - index - 1] = map(eol.getIndex());
return array;
} else if (lit instanceof MemberOfLiteral mol) {
LLValue[] array = buildGepLiteralArgs(mol.getStructurePointer(), index + 2);
final int length = array.length;
array[length - index - 2] = i32;
array[length - index - 1] = map(mol.getPointeeType(CompoundType.class), mol.getMember());
return array;
} else if (lit instanceof OffsetFromLiteral ofl) {
// offset-from always terminates a GEP
LLValue[] array = new LLValue[index + 2];
// array length == index + 2; length - index = 2
array[0] = map(ofl.getOffset().getType());
array[1] = map(ofl.getOffset());
return array;
} else {
// no other literal types can be nested GEPs, so terminate it here
LLValue[] array = new LLValue[index + 2];
// array length == index + 2; length - index = 2
array[0] = i32;
array[1] = ZERO;
return array;
}
}
private Literal innermostGepValue(Literal lit) {
// todo: type switch
if (lit instanceof ElementOfLiteral eol) {
return innermostGepValue(eol.getArrayPointer());
} else if (lit instanceof MemberOfLiteral mol) {
return innermostGepValue(mol.getStructurePointer());
} else if (lit instanceof OffsetFromLiteral ofl) {
return ofl.getBasePointer();
} else {
return lit;
}
}
public LLValue mapStatepointType(final FunctionType functionType) {
LLValue statepointType = statepointTypes.get(functionType);
if (statepointType == null) {
statepointType = Types.function(token, List.of(i64, i32, map(functionType.getPointer()), i32, i32), true);
statepointTypes.put(functionType, statepointType);
}
return statepointType;
}
public LLValue generateStatepointDecl(final FunctionType functionType) {
LLValue statepointDecl = statepointDecls.get(functionType);
if (statepointDecl == null) {
StringBuilder b = new StringBuilder(64);
b.append("llvm.experimental.gc.statepoint.");
mapTypeSuffix(b, functionType.getPointer());
final String name = b.toString();
statepointDecl = statepointDeclsByName.get(name);
if (statepointDecl == null) {
final Function decl = module.declare(name);
decl.param(i64).immarg(); // statepoint ID
decl.param(i32).immarg(); // patch byte count
// pointer to the function
final Function.Parameter param = decl.param(map(functionType.getPointer()));
if (generator.getLlvmMajor() >= 15) {
param.attribute(ParameterAttributes.elementtype(map(functionType)));
}
decl.param(i32).immarg(); // call arg count
decl.param(i32).immarg(); // flags
decl.variadic();
decl.returns(token);
statepointDecl = decl.asGlobal();
statepointDeclsByName.put(name, statepointDecl);
}
statepointDecls.put(functionType, statepointDecl);
}
return statepointDecl;
}
private void mapTypeSuffix(final StringBuilder b, final ValueType type) {
if (type instanceof BooleanType) {
b.append("i1");
} else if (type instanceof IntegerType it) {
b.append('i').append(it.getMinBits());
} else if (type instanceof PointerType pt) {
b.append("p0");
final ValueType pointeeType = pt.getPointeeType();
if (opaquePointers && ! (pointeeType instanceof InvokableType)) {
// nothing else to say
return;
}
mapTypeSuffix(b, pointeeType);
} else if (type instanceof ReferenceType || type instanceof UnresolvedType) {
b.append(opaquePointers ? "p1" : "p1i8");
} else if (type instanceof FloatType ft) {
b.append('f').append(ft.getMinBits());
} else if (type instanceof VoidType) {
b.append("isVoid");
} else if (type instanceof WordType wt) {
b.append('i').append(wt.getMinBits());
} else if (type instanceof CompoundType ct) {
b.append("s_");
final String compoundName = ct.getName();
String name;
if (ct.getTag() == CompoundType.Tag.NONE) {
String outputName = "type." + compoundName;
name = LLVM.needsQuotes(compoundName) ? LLVM.quoteString(outputName) : outputName;
} else {
String outputName = ct.getTag() + "." + compoundName;
name = LLVM.needsQuotes(compoundName) ? LLVM.quoteString(outputName) : outputName;
}
b.append(name);
b.append('s');
} else if (type instanceof VariadicType) {
// ignore
} else if (type instanceof FunctionType ft) {
b.append("f_");
mapTypeSuffix(b, ft.getReturnType());
for (ValueType parameterType : ft.getParameterTypes()) {
mapTypeSuffix(b, parameterType);
}
b.append('f');
} else {
throw new IllegalStateException("Unexpected type " + type);
}
}
public LLValue generateStatepointResultDecl(final ValueType returnType) {
LLValue resultDecl = resultDecls.get(returnType);
if (resultDecl == null) {
StringBuilder b = new StringBuilder(64);
b.append("llvm.experimental.gc.result.");
mapTypeSuffix(b, returnType);
final String name = b.toString();
resultDecl = resultDeclsByName.get(name);
if (resultDecl == null) {
final Function decl = module.declare(b.toString());
decl.param(token);
decl.returns(map(returnType));
decl.attribute(FunctionAttributes.nounwind).attribute(FunctionAttributes.readnone);
resultDecl = decl.asGlobal();
resultDeclsByName.put(name, resultDecl);
}
resultDecls.put(returnType, resultDecl);
}
return resultDecl;
}
public LLValue generateStatepointResultDeclType(final ValueType returnType) {
LLValue resultDeclType = resultDeclTypes.get(returnType);
if (resultDeclType == null) {
resultDeclType = Types.function(map(returnType), List.of(token), false);
resultDeclTypes.put(returnType, resultDeclType);
}
return resultDeclType;
}
public LLValue getRelocateDecl() {
LLValue relocateDecl = this.relocateDecl;
if (relocateDecl == null) {
final Function decl = module.declare("llvm.experimental.gc.relocate");
decl.param(token);
decl.param(i32);
decl.param(i32);
decl.attribute(FunctionAttributes.nounwind);
decl.returns(refType);
relocateDecl = this.relocateDecl = decl.asGlobal();
}
return relocateDecl;
}
public LLValue getRelocateDeclType() {
return relocateDeclType;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy