/* * Copyright (c) 2011, IETR/INSA of Rennes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 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. * * Neither the name of the IETR/INSA of Rennes 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 OWNER 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 net.sf.orcc.ir.util; import java.math.BigInteger; import java.util.Iterator; import net.sf.orcc.ir.ExprList; import net.sf.orcc.ir.Expression; import net.sf.orcc.ir.IrFactory; import net.sf.orcc.ir.OpBinary; import net.sf.orcc.ir.Type; import net.sf.orcc.ir.TypeFloat; import net.sf.orcc.ir.TypeInt; import net.sf.orcc.ir.TypeList; import net.sf.orcc.ir.TypeUint; import org.eclipse.emf.ecore.util.EcoreUtil; /** * This class defines static utility methods to deal with types. * * @author Matthieu Wipliez * */ public class TypeUtil { private static class Glb implements Unification { public static Glb instance = new Glb(); @Override public int getSize(Type t1, Type t2) { return 0; } @Override public Type getType(Type t1, Type t2) { return TypeUtil.getGlb(t1, t2); } } private static class Lub implements Unification { public static Lub instance = new Lub(); protected Type type; @Override public int getSize(Type t1, Type t2) { return 0; } @Override public Type getType(Type t1, Type t2) { type = TypeUtil.getLub(t1, t2); return type; } } private static class LubPlus1 extends Lub { public static LubPlus1 instance = new LubPlus1(); @Override public int getSize(Type t1, Type t2) { return type == null ? 0 : type.getSizeInBits() + 1; } } private static class LubSum extends Lub { public static LubSum instance = new LubSum(); @Override public int getSize(Type t1, Type t2) { return t1.getSizeInBits() + t2.getSizeInBits(); } } private static class LubSumPow extends Lub { public static LubSumPow instance = new LubSumPow(); @Override public int getSize(Type t1, Type t2) { int shift = t2.getSizeInBits(); if (shift >= 6) { // limits type size to 64 shift = 6; } return t1.getSizeInBits() + (1 << shift) - 1; } } private static class SignedLub extends Lub { public static SignedLub instance = new SignedLub(); @Override public Type getType(Type t1, Type t2) { super.getType(t1, t2); if (type.isUint()) { type = IrFactory.eINSTANCE .createTypeInt(type.getSizeInBits() + 1); } return type; } } private static interface Unification { int getSize(Type t1, Type t2); Type getType(Type t1, Type t2); } /** * Creates a new type based on the unification of t1 and t2, and clips its * size to {@link #maxSize}. * * @param t1 * a type * @param t2 * a type * @param unification * how to unify t1 and t2 */ public static Type createType(Type t1, Type t2, Unification unification) { Type type = unification.getType(t1, t2); int size = unification.getSize(t1, t2); // only set the size if it is different than 0 if (size != 0) { if (type.isInt()) { ((TypeInt) type).setSize(size); } else if (type.isUint()) { ((TypeUint) type).setSize(size); } } return type; } /** * Returns the Greatest Lower Bound of the given types. The GLB is the * smallest type of (t1, t2). When t1 and t2 have different types (e.g. one * is a int and the other is a uint), an int is returned. * * @param t1 * a type * @param t2 * another type * @return the Greatest Lower Bound of the given types */ public static Type getGlb(Type t1, Type t2) { if (t1.isBool() && t2.isBool()) { return IrFactory.eINSTANCE.createTypeBool(); } else if (t1.isInt() && t2.isInt()) { return IrFactory.eINSTANCE.createTypeInt(Math.min( ((TypeInt) t1).getSize(), ((TypeInt) t2).getSize())); } else if (t1.isUint() && t2.isUint()) { return IrFactory.eINSTANCE.createTypeUint(Math.min( ((TypeUint) t1).getSize(), ((TypeUint) t2).getSize())); } else if (t1.isInt() && t2.isUint()) { int si = ((TypeInt) t1).getSize(); int su = ((TypeUint) t2).getSize(); if (si > su) { return IrFactory.eINSTANCE.createTypeInt(su + 1); } else { return IrFactory.eINSTANCE.createTypeInt(si); } } else if (t1.isUint() && t2.isInt()) { int su = ((TypeUint) t1).getSize(); int si = ((TypeInt) t2).getSize(); if (si > su) { return IrFactory.eINSTANCE.createTypeInt(su + 1); } else { return IrFactory.eINSTANCE.createTypeInt(si); } } return null; } /** * Returns the Least Upper Bound of the given types, which is the smallest * type that can contain both types. If no such type exists (e.g. when types * are not compatible with each other), <code>null</code> is returned. * * @param t1 * a type * @param t2 * another type * @return the Least Upper Bound of the given types */ public static Type getLub(Type t1, Type t2) { if (t1 == null || t2 == null) { return null; } if (t1.isBool() && t2.isBool()) { return IrFactory.eINSTANCE.createTypeBool(); } else if (t1.isFloat() && t2.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(Math.max( ((TypeFloat) t1).getSize(), ((TypeFloat) t2).getSize())); } else if (t1.isString() && t2.isString()) { return IrFactory.eINSTANCE.createTypeString(); } else if (t1.isInt() && t2.isInt()) { return IrFactory.eINSTANCE.createTypeInt(Math.max( ((TypeInt) t1).getSize(), ((TypeInt) t2).getSize())); } else if (t1.isList() && t2.isList()) { TypeList listType1 = (TypeList) t1; TypeList listType2 = (TypeList) t2; Type type = getLub(listType1.getType(), listType2.getType()); if (type != null) { // only return a list when the underlying type is valid int size = Math.max(listType1.getSize(), listType2.getSize()); return IrFactory.eINSTANCE.createTypeList(size, type); } } else if (t1.isUint() && t2.isUint()) { return IrFactory.eINSTANCE.createTypeUint(Math.max( ((TypeUint) t1).getSize(), ((TypeUint) t2).getSize())); } else if (t1.isInt() && t2.isUint()) { int si = ((TypeInt) t1).getSize(); int su = ((TypeUint) t2).getSize(); if (si > su) { return IrFactory.eINSTANCE.createTypeInt(si); } else { return IrFactory.eINSTANCE.createTypeInt(su + 1); } } else if (t1.isUint() && t2.isInt()) { int su = ((TypeUint) t1).getSize(); int si = ((TypeInt) t2).getSize(); if (si > su) { return IrFactory.eINSTANCE.createTypeInt(si); } else { return IrFactory.eINSTANCE.createTypeInt(su + 1); } } return null; } /** * Returns the number of bits in the two's-complement representation of the * given number, including a sign bit <i>only</i> if <code>number</code> is * less than zero. * * @param number * a number * @return the number of bits in the two's-complement representation of the * given number, <i>including</i> a sign bit */ public static int getSize(BigInteger number) { int cmp = number.compareTo(BigInteger.ZERO); if (cmp == 0) { // 0 is represented as a uint(size=1) return 1; } else { int bitLength = number.bitLength(); return (cmp > 0) ? bitLength : bitLength + 1; } } /** * Returns the number of bits in the two's-complement representation of the * given number, including a sign bit <i>only</i> if <code>number</code> is * less than zero. * * @param number * a number * @return the number of bits in the two's-complement representation of the * given number, <i>including</i> a sign bit */ public static int getSize(long number) { return getSize(BigInteger.valueOf(number)); } /** * Computes the type of the given list expression. * * @param expr * a list expression * @return the type of the given list expression */ public static Type getType(ExprList expr) { Iterator<Expression> it = expr.getValue().iterator(); if (it.hasNext()) { Expression subExpr = it.next(); Type eltLubType = EcoreUtil.copy(subExpr.getType()); while (it.hasNext()) { subExpr = it.next(); Type t2 = subExpr.getType(); eltLubType = TypeUtil.getLub(eltLubType, t2); } return IrFactory.eINSTANCE.createTypeList(expr.getSize(), eltLubType); } return null; } /** * Returns the type of a binary expression whose left operand has type t1 * and right operand has type t2, and whose operator is given. * * TODO replace automatic int/uint to float casting with explicit casts * * @param op * operator * @param t1 * type of the first operand * @param t2 * type of the second operand * @return the type of the binary expression, or <code>null</code> */ public static Type getTypeBinary(OpBinary op, Type t1, Type t2) { if (t1 == null || t2 == null) { return null; } switch (op) { case BITAND: return createType(t1, t2, Glb.instance); case BITOR: case BITXOR: return createType(t1, t2, Lub.instance); case TIMES: if ((t1.isInt() || t1.isUint()) && t2.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(t2.getSizeInBits()); } else if ((t2.isInt() || t2.isUint()) && t1.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(t1.getSizeInBits()); } else if (t1.isFloat() && t2.isFloat()) { return createType(t1, t2, Lub.instance); } return createType(t1, t2, LubSum.instance); case MINUS: if ((t1.isInt() || t1.isUint()) && t2.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(t2.getSizeInBits()); } else if ((t2.isInt() || t2.isUint()) && t1.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(t1.getSizeInBits()); } else if (t1.isFloat() && t2.isFloat()) { return createType(t1, t2, Lub.instance); } return createType(t1, t2, SignedLub.instance); case PLUS: if (t1.isString() && !t2.isList() || t2.isString() && !t1.isList()) { return IrFactory.eINSTANCE.createTypeString(); } if ((t1.isInt() || t1.isUint()) && t2.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(t2.getSizeInBits()); } else if ((t2.isInt() || t2.isUint()) && t1.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(t1.getSizeInBits()); } else if (t1.isFloat() && t2.isFloat()) { return createType(t1, t2, Lub.instance); } return createType(t1, t2, LubPlus1.instance); case DIV: case DIV_INT: if ((t1.isInt() || t1.isUint()) && t2.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(t2.getSizeInBits()); } else if ((t2.isInt() || t2.isUint()) && t1.isFloat()) { return IrFactory.eINSTANCE.createTypeFloat(t1.getSizeInBits()); } else if (t1.isFloat() && t2.isFloat()) { return createType(t1, t2, Lub.instance); } case SHIFT_RIGHT: return EcoreUtil.copy(t1); case MOD: return EcoreUtil.copy(t2); case SHIFT_LEFT: return createType(t1, t2, LubSumPow.instance); case EQ: case GE: case GT: case LE: case LT: case NE: return IrFactory.eINSTANCE.createTypeBool(); case EXP: return null; case LOGIC_AND: case LOGIC_OR: return IrFactory.eINSTANCE.createTypeBool(); default: return null; } } /** * Returns <code>true</code> if type src can be converted to type dst. * * @param src * a type * @param dst * the type src should be converted to * @return <code>true</code> if type src can be converted to type dst */ public static boolean isConvertibleTo(Type src, Type dst) { if (src == null || dst == null) { return false; } if (src.isFloat() && dst.isFloat()) { return dst.getSizeInBits() >= src.getSizeInBits(); } if (src.isBool() && dst.isBool() || src.isString() && dst.isString() || (src.isInt() || src.isUint()) && (dst.isInt() || dst.isUint()) || (src.isInt() || src.isUint()) && dst.isFloat()) { return true; } if (src.isList() && dst.isList()) { TypeList typeSrc = (TypeList) src; TypeList typeDst = (TypeList) dst; // Recursively check type convertibility if (isConvertibleTo(typeSrc.getType(), typeDst.getType())) { if (typeSrc.getSizeExpr() != null && typeDst.getSizeExpr() != null) { return typeSrc.getSize() <= typeDst.getSize(); } return true; } } return false; } }