package com.oracle.truffle.llvm.parser.listeners;
import java.util.ArrayDeque;
import java.util.concurrent.atomic.AtomicInteger;
import com.oracle.truffle.llvm.parser.model.IRScope;
import com.oracle.truffle.llvm.parser.model.ModelModule;
import com.oracle.truffle.llvm.parser.model.ValueSymbol;
import com.oracle.truffle.llvm.parser.model.attributes.AttributesCodeEntry;
import com.oracle.truffle.llvm.parser.model.enums.Linkage;
import com.oracle.truffle.llvm.parser.model.enums.Visibility;
import com.oracle.truffle.llvm.parser.model.functions.FunctionDeclaration;
import com.oracle.truffle.llvm.parser.model.functions.FunctionDefinition;
import com.oracle.truffle.llvm.parser.model.functions.LazyFunctionParser;
import com.oracle.truffle.llvm.parser.model.symbols.globals.GlobalAlias;
import com.oracle.truffle.llvm.parser.model.symbols.globals.GlobalVariable;
import com.oracle.truffle.llvm.parser.model.target.TargetDataLayout;
import com.oracle.truffle.llvm.parser.model.target.TargetTriple;
import com.oracle.truffle.llvm.parser.scanner.Block;
import com.oracle.truffle.llvm.parser.scanner.LLVMScanner;
import com.oracle.truffle.llvm.parser.scanner.RecordBuffer;
import com.oracle.truffle.llvm.parser.text.LLSourceBuilder;
import com.oracle.truffle.llvm.runtime.except.LLVMParserException;
import com.oracle.truffle.llvm.runtime.types.FunctionType;
import com.oracle.truffle.llvm.runtime.types.PointerType;
import com.oracle.truffle.llvm.runtime.types.Type;
public final class Module implements ParserListener {
private final ModelModule module;
private final ParameterAttributes paramAttributes;
private final StringTable stringTable;
private int mode = 1;
private final Types types;
private final IRScope scope;
private final ArrayDeque<FunctionDefinition> functionQueue;
private final LLSourceBuilder llSource;
private final AtomicInteger index;
Module(ModelModule module, StringTable stringTable, IRScope scope, LLSourceBuilder llSource) {
this.module = module;
this.stringTable = stringTable;
this.types = new Types(module);
this.scope = scope;
this.llSource = llSource;
this.paramAttributes = new ParameterAttributes(types);
functionQueue = new ArrayDeque<>();
index = new AtomicInteger(0);
}
private boolean useStrTab() {
return mode == 2;
}
private long readNameFromStrTab(RecordBuffer buffer) {
if (useStrTab()) {
int offset = buffer.readInt();
int length = buffer.readInt();
return offset | (((long) length) << 32);
} else {
return 0;
}
}
private void assignNameFromStrTab(long name, ValueSymbol target) {
if (useStrTab()) {
int offset = (int) (name & 0xFFFFFFFF);
int length = (int) (name >> 32);
stringTable.requestName(offset, length, target);
}
}
private void createFunction(RecordBuffer buffer) {
long name = readNameFromStrTab(buffer);
Type type = types.get(buffer.readInt());
if (type instanceof PointerType) {
type = ((PointerType) type).getPointeeType();
}
buffer.skip();
final FunctionType functionType = Types.castToFunction(type);
final boolean isPrototype = buffer.readBoolean();
final Linkage linkage = Linkage.decode(buffer.read());
final AttributesCodeEntry paramAttr = paramAttributes.getCodeEntry(buffer.read());
buffer.skip();
buffer.skip();
Visibility visibility = Visibility.DEFAULT;
if (buffer.remaining() > 0) {
visibility = Visibility.decode(buffer.read());
}
if (isPrototype) {
final FunctionDeclaration function = new FunctionDeclaration(functionType, linkage, paramAttr, index.getAndIncrement());
module.addFunctionDeclaration(function);
scope.addSymbol(function, function.getType());
assignNameFromStrTab(name, function);
} else {
final FunctionDefinition function = new FunctionDefinition(functionType, linkage, visibility, paramAttr, index.getAndIncrement());
module.addFunctionDefinition(function);
scope.addSymbol(function, function.getType());
assignNameFromStrTab(name, function);
functionQueue.addLast(function);
}
}
private static final long GLOBALVAR_EXPLICICTTYPE_MASK = 0x2;
private static final long GLOBALVAR_ISCONSTANT_MASK = 0x1;
private void createGlobalVariable(RecordBuffer buffer) {
long name = readNameFromStrTab(buffer);
final long typeField = buffer.read();
final long flagField = buffer.read();
Type type = types.get(typeField);
if ((flagField & GLOBALVAR_EXPLICICTTYPE_MASK) != 0) {
type = new PointerType(type);
}
final boolean isConstant = (flagField & GLOBALVAR_ISCONSTANT_MASK) != 0;
final int initialiser = buffer.readInt();
final long linkage = buffer.read();
final int align = buffer.readInt();
buffer.skip();
long visibility = Visibility.DEFAULT.getEncodedValue();
if (buffer.remaining() > 0) {
visibility = buffer.read();
}
GlobalVariable global = GlobalVariable.create(isConstant, (PointerType) type, align, linkage, visibility, scope.getSymbols(), initialiser, index.getAndIncrement());
assignNameFromStrTab(name, global);
module.addGlobalVariable(global);
scope.addSymbol(global, global.getType());
}
private void createGlobalAliasNew(RecordBuffer buffer) {
long name = readNameFromStrTab(buffer);
final PointerType type = new PointerType(types.get(buffer.read()));
buffer.skip();
final int value = buffer.readInt();
final long linkage = buffer.read();
final GlobalAlias global = GlobalAlias.create(type, linkage, Visibility.DEFAULT.ordinal(), scope.getSymbols(), value);
assignNameFromStrTab(name, global);
module.addAlias(global);
scope.addSymbol(global, global.getType());
}
private void createGlobalAliasOld(RecordBuffer buffer) {
long name = readNameFromStrTab(buffer);
final PointerType type = Types.castToPointer(types.get(buffer.read()));
int value = buffer.readInt();
long linkage = buffer.read();
final GlobalAlias global = GlobalAlias.create(type, linkage, Visibility.DEFAULT.ordinal(), scope.getSymbols(), value);
assignNameFromStrTab(name, global);
module.addAlias(global);
scope.addSymbol(global, global.getType());
}
@Override
public ParserListener enter(Block block) {
switch (block) {
case PARAMATTR:
return paramAttributes;
case PARAMATTR_GROUP:
return paramAttributes;
case CONSTANTS:
return new Constants(types, scope);
case FUNCTION: {
throw new LLVMParserException("Function is not parsed lazily!");
}
case TYPE:
return types;
case VALUE_SYMTAB:
return new ValueSymbolTable(scope);
case METADATA:
case METADATA_KIND:
return new Metadata(types, scope);
case STRTAB:
return stringTable;
default:
return ParserListener.DEFAULT;
}
}
@Override
public void skip(Block block, LLVMScanner.LazyScanner lazyScanner) {
if (block == Block.FUNCTION) {
if (functionQueue.isEmpty()) {
throw new LLVMParserException("Missing Function Prototype in Bitcode File!");
}
final FunctionDefinition definition = functionQueue.removeFirst();
module.addFunctionParser(definition, new LazyFunctionParser(lazyScanner, scope, types, definition, mode, paramAttributes, llSource));
} else {
ParserListener.super.skip(block, lazyScanner);
}
}
private static final int MODULE_VERSION = 1;
private static final int MODULE_TARGET_TRIPLE = 2;
private static final int MODULE_TARGET_DATALAYOUT = 3;
private static final int MODULE_GLOBAL_VARIABLE = 7;
private static final int MODULE_FUNCTION = 8;
private static final int MODULE_ALIAS_OLD = 9;
private static final int MODULE_ALIAS = 14;
@Override
public void record(RecordBuffer buffer) {
switch (buffer.getId()) {
case MODULE_VERSION:
mode = buffer.readInt();
break;
case MODULE_TARGET_TRIPLE:
module.addTargetInformation(new TargetTriple(buffer.readString()));
break;
case MODULE_TARGET_DATALAYOUT:
final TargetDataLayout layout = TargetDataLayout.fromString(buffer.readString());
module.setTargetDataLayout(layout);
break;
case MODULE_GLOBAL_VARIABLE:
createGlobalVariable(buffer);
break;
case MODULE_FUNCTION:
createFunction(buffer);
break;
case MODULE_ALIAS:
createGlobalAliasNew(buffer);
break;
case MODULE_ALIAS_OLD:
createGlobalAliasOld(buffer);
break;
default:
break;
}
}
}