/* * Copyright (c) 2016, Oracle and/or its affiliates. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other materials provided * with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.oracle.truffle.llvm.parser.listeners; import java.util.ArrayList; import java.util.List; import com.oracle.truffle.llvm.parser.model.blocks.InstructionBlock; import com.oracle.truffle.llvm.parser.model.generators.FunctionGenerator; import com.oracle.truffle.llvm.parser.model.symbols.Symbols; import com.oracle.truffle.llvm.parser.records.FunctionRecord; import com.oracle.truffle.llvm.parser.records.Records; import com.oracle.truffle.llvm.parser.scanner.Block; import com.oracle.truffle.llvm.runtime.LLVMLogger; import com.oracle.truffle.llvm.runtime.types.AggregateType; import com.oracle.truffle.llvm.runtime.types.ArrayType; 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.PrimitiveType.PrimitiveKind; import com.oracle.truffle.llvm.parser.metadata.MDLocation; import com.oracle.truffle.llvm.runtime.types.StructureType; import com.oracle.truffle.llvm.runtime.types.Type; import com.oracle.truffle.llvm.runtime.types.VectorType; import com.oracle.truffle.llvm.runtime.types.VoidType; public final class Function implements ParserListener { private static final int INSERT_VALUE_MAX_ARGS = 3; private final FunctionGenerator generator; protected final Types types; protected final List<Type> symbols; private final int mode; InstructionBlock instructionBlock = null; boolean isLastBlockTerminated = true; private MDLocation lastLocation = null; private final List<Integer> implicitIndices = new ArrayList<>(); Function(Types types, List<Type> symbols, FunctionGenerator generator, int mode) { this.types = types; this.symbols = symbols; this.generator = generator; this.mode = mode; } @Override public ParserListener enter(Block block) { switch (block) { case CONSTANTS: return new Constants(types, symbols, generator); case VALUE_SYMTAB: return new ValueSymbolTable(generator); case METADATA: case METADATA_ATTACHMENT: case METADATA_KIND: return new Metadata(types, generator); default: LLVMLogger.info("Entering Unknown Block inside Function: " + block); return ParserListener.DEFAULT; } } @Override public void exit() { generator.exitFunction(); } @Override public void record(long id, long[] args) { FunctionRecord record = FunctionRecord.decode(id); // debug locations can occur after terminating instructions, we process them before we // replace the old block switch (record) { case DEBUG_LOC: parseDebugLocation(args); // intentional fallthrough case DEBUG_LOC_AGAIN: applyDebugLocation(); return; case DECLAREBLOCKS: generator.allocateBlocks((int) args[0]); return; default: break; } if (isLastBlockTerminated) { instructionBlock = generator.generateBlock(); isLastBlockTerminated = false; } switch (record) { case BINOP: createBinaryOperation(args); break; case CAST: createCast(args); break; case GEP_OLD: createGetElementPointerOld(args, false); break; case EXTRACTELT: createExtractElement(args); break; case INSERTELT: createInsertElement(args); break; case SHUFFLEVEC: createShuffleVector(args); break; case RET: createReturn(args); break; case BR: createBranch(args); break; case SWITCH: createSwitch(args); break; case UNREACHABLE: createUnreachable(args); break; case PHI: createPhi(args); break; case ALLOCA: createAlloca(args); break; case LOAD: createLoad(args); break; case STORE_OLD: createStoreOld(args); break; case EXTRACTVAL: createExtractValue(args); break; case INSERTVAL: createInsertValue(args); break; case CMP2: createCompare2(args); break; case VSELECT: createSelect(args); break; case INBOUNDS_GEP_OLD: createGetElementPointerOld(args, true); break; case INDIRECTBR: createIndirectBranch(args); break; case CALL: createFunctionCall(args); break; case INVOKE: createInvoke(args); break; case LANDINGPAD: createLandingpad(args); break; case RESUME: createResume(args); break; case GEP: createGetElementPointer(args); break; case STORE: createStore(args); break; case LOADATOMIC: createLoadAtomic(args); break; case STOREATOMIC: createAtomicStore(args); break; case CMPXCHG_OLD: case CMPXCHG: createCompareExchange(args, record); break; default: throw new UnsupportedOperationException("Unsupported Record: " + record); } } private static final int INVOKE_HASEXPLICITFUNCTIONTYPE_SHIFT = 13; private void createInvoke(long[] args) { int i = 0; i++; // parameter attributes final long ccInfo = args[i++]; final int normalSuccessorBlock = (int) (args[i++]); final int unwindSuccessorBlock = (int) (args[i++]); FunctionType functionType = null; if (((ccInfo >> INVOKE_HASEXPLICITFUNCTIONTYPE_SHIFT) & 1) != 0) { functionType = (FunctionType) types.get(args[i++]); } final int target = getIndex(args[i++]); final Type calleeType; if (target <= symbols.size()) { calleeType = symbols.get(target); } else { calleeType = types.get(args[i++]); } if (functionType == null) { if (calleeType instanceof PointerType) { functionType = (FunctionType) ((PointerType) calleeType).getPointeeType(); } else { throw new AssertionError("Cannot find Type of invoked function!"); } } final int[] arguments = new int[args.length - i]; for (int j = 0; i < args.length; i++, j++) { arguments[j] = getIndex(args[i]); } final Type returnType = functionType.getReturnType(); instructionBlock.createInvoke(returnType, target, arguments, normalSuccessorBlock, unwindSuccessorBlock); if (!(returnType instanceof VoidType)) { symbols.add(returnType); } isLastBlockTerminated = true; } private void createResume(long[] args) { int i = 0; final int val = getIndex(args[i++]); final Type type; if (val < symbols.size()) { type = symbols.get(val); } else { type = types.get(args[i]); } instructionBlock.createResume(type); isLastBlockTerminated = true; } private void createLandingpad(long[] args) { int i = 0; final Type type = types.get(args[i++]); final boolean isCleanup = args[i++] != 0; final int numClauses = (int) args[i++]; long[] clauseKinds = new long[numClauses]; // catch = 0, filter = 1 long[] clauseTypes = new long[numClauses]; for (int j = 0; j < numClauses; j++) { clauseKinds[j] = args[i++]; clauseTypes[j] = getIndex(args[i++]); } symbols.add(type); instructionBlock.createLandingpad(type, isCleanup, clauseKinds, clauseTypes); } private static final int CALL_HAS_FMF_SHIFT = 17; private static final int CALL_HAS_EXPLICITTYPE_SHIFT = 15; private void createFunctionCall(long[] args) { int i = 1; final long ccinfo = args[i++]; FunctionType functionType = null; int callee = -1; Type calleeType = null; if (((ccinfo >> CALL_HAS_FMF_SHIFT) & 1) != 0) { i++; // fast math flags } if (((ccinfo >> CALL_HAS_EXPLICITTYPE_SHIFT) & 1) != 0) { functionType = (FunctionType) types.get(args[i++]); } callee = getIndex(args[i++]); if (callee >= symbols.size()) { calleeType = types.get(args[i++]); } else { calleeType = symbols.get(callee); } if (functionType == null) { if (calleeType instanceof FunctionType) { functionType = (FunctionType) calleeType; } else { functionType = (FunctionType) ((PointerType) calleeType).getPointeeType(); } } final int[] arguments = new int[args.length - i]; for (int j = 0; i < args.length; i++, j++) { arguments[j] = getIndex(args[i]); } final Type returnType = functionType.getReturnType(); instructionBlock.createCall(returnType, callee, arguments); if (returnType != VoidType.INSTANCE) { symbols.add(returnType); } } private static final long SWITCH_CASERANGE_SHIFT = 16; private static final long SWITCH_CASERANGE_FLAG = 0x4B5; private void createSwitch(long[] args) { int i = 0; if ((args[0] >> SWITCH_CASERANGE_SHIFT) == SWITCH_CASERANGE_FLAG) { i++; // indicator i++; // type final int cond = getIndex(args[i++]); final int defaultBlock = (int) args[i++]; final int count = (int) args[i++]; final long[] caseConstants = new long[count]; final int[] caseBlocks = new int[count]; for (int j = 0; j < count; j++) { i += 2; caseConstants[j] = Records.toSignedValue(args[i++]); caseBlocks[j] = (int) args[i++]; } instructionBlock.createSwitchOld(cond, defaultBlock, caseConstants, caseBlocks); } else { i++; // type final int cond = getIndex(args[i++]); final int defaultBlock = (int) args[i++]; final int count = (args.length - i) >> 1; final int[] caseValues = new int[count]; final int[] caseBlocks = new int[count]; for (int j = 0; j < count; j++) { caseValues[j] = getIndexAbsolute(args[i++]); caseBlocks[j] = (int) args[i++]; } instructionBlock.createSwitch(cond, defaultBlock, caseValues, caseBlocks); } isLastBlockTerminated = true; } private static final long ALLOCA_INMASK = 1L << 5; private static final long ALLOCA_EXPLICITTYPEMASK = 1L << 6; private static final long ALLOCA_SWIFTERRORMASK = 1L << 7; private static final long ALLOCA_FLAGSMASK = ALLOCA_INMASK | ALLOCA_EXPLICITTYPEMASK | ALLOCA_SWIFTERRORMASK; private void createAlloca(long[] args) { int i = 0; final long typeRecord = args[i++]; i++; // type of count final int count = getIndexAbsolute(args[i++]); final long alignRecord = args[i]; final int align = getAlign(alignRecord & ~ALLOCA_FLAGSMASK); Type type = types.get(typeRecord); if ((alignRecord & ALLOCA_EXPLICITTYPEMASK) != 0L) { type = new PointerType(type); } else if (!(type instanceof PointerType)) { throw new AssertionError("Alloca must have PointerType!"); } instructionBlock.createAllocation(type, count, align); symbols.add(type); } private static final int LOAD_ARGS_EXPECTED_AFTER_TYPE = 3; private void createLoad(long[] args) { int i = 0; final int src = getIndex(args[i++]); final Type srcType; if (src >= symbols.size()) { srcType = types.get(args[i++]); } else { srcType = symbols.get(src); } final Type opType; if (i + LOAD_ARGS_EXPECTED_AFTER_TYPE == args.length) { opType = types.get(args[i++]); } else { opType = ((PointerType) srcType).getPointeeType(); } final int align = getAlign(args[i++]); final boolean isVolatile = args[i] != 0; instructionBlock.createLoad(opType, src, align, isVolatile); symbols.add(opType); } private static final int LOADATOMIC_ARGS_EXPECTED_AFTER_TYPE = 5; private void createLoadAtomic(long[] args) { int i = 0; final int src = getIndex(args[i++]); final Type srcType; if (src >= symbols.size()) { srcType = types.get(args[i++]); } else { srcType = symbols.get(src); } final Type opType; if (i + LOADATOMIC_ARGS_EXPECTED_AFTER_TYPE == args.length) { opType = types.get(args[i++]); } else { opType = ((PointerType) srcType).getPointeeType(); } final int align = getAlign(args[i++]); final boolean isVolatile = args[i++] != 0; final long atomicOrdering = args[i++]; final long synchronizationScope = args[i]; instructionBlock.createAtomicLoad(opType, src, align, isVolatile, atomicOrdering, synchronizationScope); symbols.add(opType); } private void createCompareExchange(long[] args, FunctionRecord record) { final Symbols functionSymbols = instructionBlock.getFunctionSymbols(); int i = 0; final Type ptrType; final int ptr = getIndex(args[i]); if (ptr >= functionSymbols.getSize()) { ptrType = types.get(args[++i]); } else { ptrType = symbols.get(ptr); } final int cmp = getIndex(args[++i]); if (record == FunctionRecord.CMPXCHG && cmp >= functionSymbols.getSize()) { ++i; // type of cmp } final int replace = getIndex(args[++i]); final boolean isVolatile = args[++i] != 0; final long successOrdering = args[++i]; final long synchronizationScope = args[++i]; final long failureOrdering = i < args.length - 1 ? args[++i] : -1L; final boolean addExtractValue = i >= args.length - 1; final boolean isWeak = addExtractValue || (args[++i] != 0); final Type type = findCmpxchgResultType(((PointerType) ptrType).getPointeeType()); instructionBlock.createCompareExchange(type, ptr, cmp, replace, isVolatile, successOrdering, synchronizationScope, failureOrdering, isWeak); symbols.add(type); if (addExtractValue) { // in older llvm versions cmpxchg just returned the new value at the pointer, to emulate // this we have to add an extractelvalue instruction. llvm does the same thing createExtractValue(new long[]{1, 0}); implicitIndices.add(symbols.size() - 1); // register the implicit index LLVMLogger.info("cmpxchg implicitly inserted an extractelement instruction."); } } private static final int CMPXCHG_TYPE_LENGTH = 2; private static final int CMPXCHG_TYPE_ELEMENTTYPE = 0; private static final int CMPXCHG_TYPE_BOOLTYPE = 1; private Type findCmpxchgResultType(Type elementType) { // cmpxchg is the only instruction that does not directly reference its return type in the // type table for (Type t : types) { if (t != null && t instanceof StructureType) { final Type[] elts = ((StructureType) t).getElementTypes(); if (elts.length == CMPXCHG_TYPE_LENGTH && elementType == elts[CMPXCHG_TYPE_ELEMENTTYPE] && PrimitiveType.I1 == elts[CMPXCHG_TYPE_BOOLTYPE]) { return t; } } } // the type may not exist if the value is not being used return new StructureType(true, new Type[]{elementType, PrimitiveType.I1}); } private void parseDebugLocation(long[] args) { // if e.g. the previous instruction was @llvm.debug.declare this will be the location of the // declaration of the variable in the source file lastLocation = MDLocation.createFromFunctionArgs(args, generator.getMetadata()); } private void applyDebugLocation() { final int lastInstructionIndex = instructionBlock.getInstructionCount() - 1; instructionBlock.getInstruction(lastInstructionIndex).setDebugLocation(lastLocation); } private void createAtomicStore(long[] args) { int i = 0; final int destination = getIndex(args[i++]); if (destination > symbols.size()) { i++; } final int source = getIndex(args[i++]); if (source > symbols.size()) { i++; } final int align = getAlign(args[i++]); final boolean isVolatile = args[i++] != 0; final long atomicOrdering = args[i++]; final long synchronizationScope = args[i]; instructionBlock.createAtomicStore(destination, source, align, isVolatile, atomicOrdering, synchronizationScope); } private void createBinaryOperation(long[] args) { int i = 0; Type type; int lhs = getIndex(args[i++]); if (lhs < symbols.size()) { type = symbols.get(lhs); } else { type = types.get(args[i++]); } int rhs = getIndex(args[i++]); int opcode = (int) args[i++]; int flags = i < args.length ? (int) args[i] : 0; instructionBlock.createBinaryOperation(type, opcode, flags, lhs, rhs); symbols.add(type); } private void createBranch(long[] args) { if (args.length == 1) { instructionBlock.createBranch((int) args[0]); } else { instructionBlock.createBranch(getIndex(args[2]), (int) args[0], (int) args[1]); } isLastBlockTerminated = true; } private void createCast(long[] args) { int i = 0; int value = getIndex(args[i++]); if (value >= symbols.size()) { i++; } Type type = types.get(args[i++]); int opcode = (int) args[i]; instructionBlock.createCast(type, opcode, value); symbols.add(type); } private void createCompare2(long[] args) { int i = 0; Type operandType; int lhs = getIndex(args[i++]); if (lhs < symbols.size()) { operandType = symbols.get(lhs); } else { operandType = types.get(args[i++]); } int rhs = getIndex(args[i++]); int opcode = (int) args[i]; Type type = operandType instanceof VectorType ? new VectorType(PrimitiveType.I1, ((VectorType) operandType).getNumberOfElements()) : PrimitiveType.I1; instructionBlock.createCompare(type, opcode, lhs, rhs); symbols.add(type); } private void createExtractElement(long[] args) { int vector = getIndex(args[0]); int index = getIndex(args[1]); Type type = ((VectorType) symbols.get(vector)).getElementType(); instructionBlock.createExtractElement(type, vector, index); symbols.add(type); } private void createExtractValue(long[] args) { int aggregate = getIndex(args[0]); int index = (int) args[1]; if (args.length != 2) { // This is supported in neither parser. throw new UnsupportedOperationException("Multiple indices are not yet supported!"); } Type type = ((AggregateType) symbols.get(aggregate)).getElementType(index); instructionBlock.createExtractValue(type, aggregate, index); symbols.add(type); } private void createGetElementPointer(long[] args) { int i = 0; boolean isInbounds = args[i++] != 0; i++; // we do not use this parameter int pointer = getIndex(args[i++]); Type base; if (pointer < symbols.size()) { base = symbols.get(pointer); } else { base = types.get(args[i++]); } int[] indices = getIndices(args, i); Type type = new PointerType(getElementPointerType(base, indices)); instructionBlock.createGetElementPointer( type, pointer, indices, isInbounds); symbols.add(type); } private void createGetElementPointerOld(long[] args, boolean isInbounds) { int i = 0; int pointer = getIndex(args[i++]); Type base; if (pointer < symbols.size()) { base = symbols.get(pointer); } else { base = types.get(args[i++]); } int[] indices = getIndices(args, i); Type type = new PointerType(getElementPointerType(base, indices)); instructionBlock.createGetElementPointer( type, pointer, indices, isInbounds); symbols.add(type); } private void createIndirectBranch(long[] args) { int address = getIndex(args[1]); int[] successors = new int[args.length - 2]; for (int i = 0; i < successors.length; i++) { successors[i] = (int) args[i + 2]; } instructionBlock.createIndirectBranch(address, successors); isLastBlockTerminated = true; } private void createInsertElement(long[] args) { int vector = getIndex(args[0]); int index = getIndex(args[2]); int value = getIndex(args[1]); Type symbol = symbols.get(vector); instructionBlock.createInsertElement(symbol, vector, index, value); symbols.add(symbol); } private void createInsertValue(long[] args) { int aggregate = getIndex(args[0]); int index = (int) args[2]; int value = getIndex(args[1]); if (args.length != INSERT_VALUE_MAX_ARGS) { // This is supported in neither parser. throw new UnsupportedOperationException("Multiple indices are not yet supported!"); } Type symbol = symbols.get(aggregate); instructionBlock.createInsertValue(symbol, aggregate, index, value); symbols.add(symbol); } private void createPhi(long[] args) { Type type = types.get(args[0]); int count = (args.length) - 1 >> 1; int[] values = new int[count]; int[] blocks = new int[count]; for (int i = 0, j = 1; i < count; i++) { values[i] = getIndex(Records.toSignedValue(args[j++])); blocks[i] = (int) args[j++]; } instructionBlock.createPhi(type, values, blocks); symbols.add(type); } private void createReturn(long[] args) { if (args.length == 0 || args[0] == 0) { instructionBlock.createReturn(); } else { instructionBlock.createReturn(getIndex(args[0])); } isLastBlockTerminated = true; } private void createSelect(long[] args) { int i = 0; Type type; int trueValue = getIndex(args[i++]); if (trueValue < symbols.size()) { type = symbols.get(trueValue); } else { type = types.get(args[i++]); } int falseValue = getIndex(args[i++]); int condition = getIndex(args[i]); instructionBlock.createSelect(type, condition, trueValue, falseValue); symbols.add(type); } private void createShuffleVector(long[] args) { int vector1 = getIndex(args[0]); int vector2 = getIndex(args[1]); int mask = getIndex(args[2]); PrimitiveType subtype = ((VectorType) symbols.get(vector1)).getElementType(); int length = ((VectorType) symbols.get(mask)).getNumberOfElements(); Type type = new VectorType(subtype, length); instructionBlock.createShuffleVector(type, vector1, vector2, mask); symbols.add(type); } private void createStore(long[] args) { int i = 0; int destination = getIndex(args[i++]); if (destination > symbols.size()) { i++; } int source = getIndex(args[i++]); if (source > symbols.size()) { i++; } int align = getAlign(args[i++]); boolean isVolatile = args[i] != 0; instructionBlock.createStore(destination, source, align, isVolatile); } private void createStoreOld(long[] args) { int i = 0; int destination = getIndex(args[i++]); if (destination > symbols.size()) { i++; } int source = getIndex(args[i++]); int align = getAlign(args[i++]); boolean isVolatile = args[i] != 0; instructionBlock.createStore(destination, source, align, isVolatile); } private void createUnreachable(@SuppressWarnings("unused") long[] args) { instructionBlock.createUnreachable(); isLastBlockTerminated = true; } private static int getAlign(long argument) { return (int) argument & (Long.SIZE - 1); } private Type getElementPointerType(Type type, int[] indices) { Type elementType = type; for (int indice : indices) { if (elementType instanceof PointerType) { elementType = ((PointerType) elementType).getPointeeType(); } else if (elementType instanceof ArrayType) { elementType = ((ArrayType) elementType).getElementType(); } else if (elementType instanceof VectorType) { elementType = ((VectorType) elementType).getElementType(); } else { StructureType structure = (StructureType) elementType; Type idx = symbols.get(indice); if (!(idx instanceof PrimitiveType)) { throw new IllegalStateException("Cannot infer structure element from " + idx); } Number index = (Number) ((PrimitiveType) idx).getConstant(); assert ((PrimitiveType) idx).getPrimitiveKind() == PrimitiveKind.I32; elementType = structure.getElementType(index.intValue()); } } return elementType; } protected int getIndex(long index) { if (mode == 0) { return getIndexAbsolute(index); } else { return getIndexRelative(index); } } private int[] getIndices(long[] arguments, int from) { return getIndices(arguments, from, arguments.length); } private int[] getIndices(long[] arguments, int from, int to) { int[] indices = new int[to - from]; for (int i = 0; i < indices.length; i++) { indices[i] = getIndex(arguments[from + i]); } return indices; } protected int getIndexAbsolute(long index) { long actualIndex = index; for (int i = 0; i < implicitIndices.size() && implicitIndices.get(i) <= actualIndex; i++) { actualIndex++; } return (int) actualIndex; } protected int getIndexRelative(long index) { long actualIndex = symbols.size() - index; for (int i = implicitIndices.size() - 1; i >= 0 && implicitIndices.get(i) > actualIndex; i--) { actualIndex--; } return (int) actualIndex; } }