/* * xtc - The eXTensible Compiler * Copyright (C) 2007-2009 Robert Grimm * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.type; import java.math.BigInteger; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import xtc.Constants; import xtc.Limits; import xtc.tree.Attribute; import xtc.util.Utilities; /** * Common type operations for the C language. * * @author Robert Grimm * @version $Revision: 1.44 $ */ public class C { /** Create a new instance. */ public C() { /* Nothing to do. */ } // ========================================================================= /** The flag for GCC's legacy structure layout. */ public static final boolean GCC_LEGACY_LAYOUT; static { GCC_LEGACY_LAYOUT = Limits.COMPILER_NAME.equals("gcc") && (Limits.COMPILER_VERSION_MAJOR < 4 || (Limits.COMPILER_VERSION_MAJOR == 4 && Limits.COMPILER_VERSION_MINOR == 0)); } // ========================================================================= /** * The canonical implicit int type. In K&R C, a missing type * specifier is treated as an int. We preserve knowledge about this * lack of type specifier through this type, which has a {@link * Constants#ATT_IMPLICIT} attribute. */ public static final IntegerT IMPLICIT = new IntegerT(NumberT.Kind.INT); static { IMPLICIT.addAttribute(Constants.ATT_IMPLICIT); IMPLICIT.seal(); } /** The integer kind of the sizeof type. */ protected static final NumberT.Kind KIND_SIZEOF = IntegerT.fromRank(Limits.SIZEOF_RANK, false); /** The canonical sizeof type. */ public static final IntegerT SIZEOF = new IntegerT(KIND_SIZEOF); static { SIZEOF.seal(); } /** The integer kind of the pointer difference type. */ protected static final NumberT.Kind KIND_PTR_DIFF = IntegerT.fromRank(Limits.PTRDIFF_RANK, true); /** The canonical pointer difference type. */ public static final IntegerT PTR_DIFF = new IntegerT(KIND_PTR_DIFF); static { PTR_DIFF.seal(); } /** The number kind of the wchar_t type. */ protected static final NumberT.Kind KIND_WCHAR = IntegerT.fromRank(Limits.WCHAR_RANK, Limits.IS_WCHAR_SIGNED); /** The canonical wide char type. */ public static final IntegerT WCHAR = new IntegerT(KIND_WCHAR); static { WCHAR.seal(); } // ========================================================================= /** * Determine whether the specified type is a char. * * @param type The type. * @return <code>true</code> if the type is a char. */ public boolean isChar(Type type) { if (type.hasEnum()) return false; type = type.resolve(); if (type.isInteger()) { switch (type.toInteger().getKind()) { case CHAR: case S_CHAR: case U_CHAR: return true; default: return false; } } else { return false; } } /** * Determine whether the specified type is a wide char. * * @param type The type. * @return <code>true</code> if the type is a wide char. */ public boolean isWideChar(Type type) { if (type.hasEnum()) return false; type = type.resolve(); if (type.isInteger()) { return NumberT.equal(KIND_WCHAR, type.toInteger().getKind()); } else { return false; } } /** * Determine whether the specified type is a string. * * @param type The type. * @return <code>true</code> if the type is a string. */ public boolean isString(Type type) { type = type.resolve(); return type.isArray() && isChar(type.toArray().getType()); } /** * Determine whether the specified type is a wide string. * * @param type The type. * @return <code>true</code> if the type is a wide string. */ public boolean isWideString(Type type) { type = type.resolve(); return type.isArray() && isWideChar(type.toArray().getType()); } /** * Determine whether the specified type is a string literal. * * @param type The type. * @return <code>true</code> if the type is a string literal. */ public boolean isStringLiteral(Type type) { return (isString(type) || isWideString(type)) && type.hasConstant(); } // ========================================================================= /** * Determine whether the specified type is integral. * * @param type The type. * @return <code>true</code> if the specified type is integral. */ public boolean isIntegral(Type type) { switch (type.tag()) { case BOOLEAN: case INTEGER: return true; default: return false; } } /** * Determine whether the specified type is real. * * @param type The type. * @return <code>true</code> if the specified type is real. */ public boolean isReal(Type type) { switch (type.tag()) { case BOOLEAN: case INTEGER: return true; case FLOAT: switch (type.resolve().toFloat().getKind()) { case FLOAT_COMPLEX: case DOUBLE_COMPLEX: case LONG_DOUBLE_COMPLEX: return false; default: return true; } default: return false; } } /** * Determine whether the specified type is arithmetic. * * @param type The type. * @return <code>true</code> if the specified type is arithmetic. */ public boolean isArithmetic(Type type) { switch (type.tag()) { case BOOLEAN: case INTEGER: case FLOAT: return true; default: return false; } } /** * Determine whether the specified type is scalar. * * @param type The type. * @return <code>true</code> if the specified type is scalar. */ public boolean isScalar(Type type) { switch (type.tag()) { case BOOLEAN: case INTEGER: case FLOAT: case POINTER: return true; default: return false; } } /** * Determine whether the specified type is incomplete. * * <p />Per C99 6.2.5, a type is incomplete if it does not contain * sufficient information for determining its size. However, per * C99 6.7.2.1, a struct type with more than one named member may * have an incomplete type as its last member, while still being * considered complete. * * @param type The type. * @return <code>true</code> if the type is incomplete. */ public boolean isIncomplete(Type type) { // Handle alias and enum types first since they are wrapped and do // not have a tag. while (type.isWrapped()) { switch (type.wtag()) { case ALIAS: return null == type.toAlias().getType(); case ENUM: return null == type.toEnum().getMembers(); default: type = type.toWrapped().getType(); } } // The type has been resolved. switch (type.tag()) { case VOID: // Void always is incomplete. return true; case ARRAY: { // An array is incomplete if (1) it is not variable and has no // lenght, (2) it has an incomplete member type, or (3) it has a // trailing array. ArrayT a = type.toArray(); return (((! a.isVarLength()) && (! a.hasLength())) || isIncomplete(a.getType()) || hasTrailingArray(a.getType())); } case STRUCT: { // A struct is incomplete if (1) it has no members, (2) any // member but the last member is incomplete, (3) the last member // is not an array but is incomplete, or (4) the last member is // an array with an incomplete element type. List<VariableT> members = type.toStruct().getMembers(); if (null == members) return true; for (Iterator<VariableT> iter = members.iterator(); iter.hasNext(); ) { VariableT member = iter.next(); if (iter.hasNext() || (Type.Tag.ARRAY != member.tag())) { // In general, members may not be incomplete. We allow // struct members with trailing incomplete arrays because // GCC allows them. if (isIncomplete(member)) return true; } else { // The last member is an array. We allow struct element // types with trailing incomplete arrays because GCC allows // them. ArrayT array = member.resolve().toArray(); if (isIncomplete(array.getType())) return true; } } return false; } case UNION: { // A union is incomplete if (1) it has no members or (2) any // member is incomplete. List<VariableT> members = type.toUnion().getMembers(); if (null == members) return true; for (Type t : members) { if (isIncomplete(t)) return true; } return false; } default: return false; } } /** * Determine whether the specified type has a trailing array. This * method checks whether the specified type is either a struct type * with an incomplete array as its last member or a union containing * such a member. * * @param type The type. * @return <code>true</code> if the type has a trailing array. */ public boolean hasTrailingArray(Type type) { switch (type.tag()) { case STRUCT: { // A struct has a trailing array if its last member is a fixed // length array without a concrete length. List<VariableT> members = type.resolve().toStruct().getMembers(); if (null != members) { int size = members.size(); Type last = 0 < size ? members.get(size-1) : null; if ((null != last) && (Type.Tag.ARRAY == last.tag())) { ArrayT a = last.resolve().toArray(); return (! a.isVarLength()) && (! a.hasLength()); } } return false; } case UNION: { // A union has a trailing array if any member has a trailing // array. List<VariableT> members = type.resolve().toUnion().getMembers(); if (null != members) { for (Type t : members) { if (hasTrailingArray(t)) return true; } } return false; } default: return false; } } /** * Determine whether the specified type is variably modified. * Consistent with C99 6.7.5-3, this method checks whether the type * contains a variable length array type. * * @param type The type. * @return <code>true</code> if the type is variably modified. */ public boolean isVariablyModified(Type type) { switch (type.tag()) { case POINTER: return isVariablyModified(type.resolve().toPointer().getType()); case ARRAY: ArrayT a = type.resolve().toArray(); return a.isVarLength() || isVariablyModified(a.getType()); default: return false; } } /** * Determine whether the specified type is qualified as constant. * Consistent with C99 6.3.2.1, this method checks whether the type * has a {@link Constants#ATT_CONSTANT} attribute and, if it is an * array, struct, or union, whether any member or element of the * type has that attribute. * * @param type The type. * @return <code>true</code> if the specified type is constant * qualified. */ public boolean isConstant(Type type) { if (type.hasAttribute(Constants.ATT_CONSTANT)) return true; switch (type.tag()) { case ARRAY: type = type.resolve().toArray().getType(); return (null == type) ? false : isConstant(type); case STRUCT: case UNION: for (Type member : type.toTagged().getMembers()) { if (isConstant(member)) return true; } return false; default: return false; } } /** * Determine whether the specified type represents a modifiable * lvalue. Consistent with C99 6.3.2.1, this method checks that the * type represents an lvalue, is not incomplete, and is not * qualified as constant. For struct and union types, this method * also checks that no member is qualified as constant. * * @param type The type. * @return <code>true</code> if the type represents a modifiable * lvalue. */ public boolean isModifiable(Type type) { if (! type.hasShape()) return false; if (isIncomplete(type)) return false; if (Type.Tag.ARRAY == type.tag()) return false; return ! isConstant(type); } // ========================================================================= /** * Determine whether this type has a constant reference. This * method takes the pointer decay of arrays and functions into * account and returns <code>true</code> either if the specified * type has a constant reference or if this type is an array or * function and has a constant reference. * * @param type The type. * @return <code>true</code> if the type has a constant reference. */ public boolean hasConstRef(Type type) { if (type.hasConstant() && type.getConstant().isReference()) return true; switch (type.tag()) { case ARRAY: case FUNCTION: return type.hasShape() && type.getShape().isConstant(); default: return false; } } /** * Get the specified type's constant reference. * * @param type The type. * @return The constant reference. * @throws IllegalArgumentException Signals that the type does not * have a constant reference. */ public Reference getConstRef(Type type) { if (type.hasConstant()) { Constant constant = type.getConstant(); if (! constant.isReference()) { throw new IllegalArgumentException("Constant not a reference " + type); } return constant.refValue(); } switch (type.tag()) { case ARRAY: case FUNCTION: if (type.hasShape()) { Reference ref = type.getShape(); if (! ref.isConstant()) { throw new IllegalArgumentException("Shaped not constant " + type); } return ref; } // Fall through. default: throw new IllegalArgumentException("Type without constant reference " + type); } } // ========================================================================= /** * Determine whether the specified type has any qualifiers. * * @param type The type. * @return <code>true</code> if the type has any qualifiers. */ public boolean hasQualifiers(Type type) { return (type.hasAttribute(Constants.ATT_CONSTANT) || type.hasAttribute(Constants.ATT_RESTRICT) || type.hasAttribute(Constants.ATT_VOLATILE)); } /** * Determine whether the specified type has at least the qualifiers * of the specified template. * * @param type The type. * @param template The template. * @return <code>true</code> if the type has at least the template's * qualifiers. */ public boolean hasQualifiers(Type type, Type template) { if (template.hasAttribute(Constants.ATT_CONSTANT) && (! type.hasAttribute(Constants.ATT_CONSTANT))) { return false; } if (template.hasAttribute(Constants.ATT_RESTRICT) && (! type.hasAttribute(Constants.ATT_RESTRICT))) { return false; } if (template.hasAttribute(Constants.ATT_VOLATILE) && (! type.hasAttribute(Constants.ATT_VOLATILE))) { return false; } return true; } /** * Determine whether the specified type has the same qualifiers as * the specified template. * * @param type The type. * @param template The template. * @return <code>true</code> if the type has the same qualifiers * as the template. */ public boolean hasSameQualifiers(Type type, Type template) { return ((type.hasAttribute(Constants.ATT_CONSTANT) == template.hasAttribute(Constants.ATT_CONSTANT)) && (type.hasAttribute(Constants.ATT_RESTRICT) == template.hasAttribute(Constants.ATT_RESTRICT)) && (type.hasAttribute(Constants.ATT_VOLATILE) == template.hasAttribute(Constants.ATT_VOLATILE))); } /** * Qualify the specified type with the qualifiers of the specified * template. If the template has any qualifiers, this method first * {@link Type#annotate() annotates} the specified type first. * * @param type The type. * @param template The template. * @return The qualified type. */ public Type qualify(Type type, Type template) { if (! hasQualifiers(template)) return type; type = type.annotate(); if (template.hasAttribute(Constants.ATT_CONSTANT)) { type = type.attribute(Constants.ATT_CONSTANT); } if (template.hasAttribute(Constants.ATT_RESTRICT)) { type = type.attribute(Constants.ATT_RESTRICT); } if (template.hasAttribute(Constants.ATT_VOLATILE)) { type = type.attribute(Constants.ATT_VOLATILE); } return type; } // ========================================================================= /** * Reattribute the specified type with the specified template's GCC * attributes. * * @param type The type. * @param template The template. * @return The reattributed type. */ public Type reattribute(Type type, Type template) { boolean annotated = false; do { // If the template has any attributes, check them out. if (template.hasAttributes()) { for (Attribute att : template.attributes()) { if (Constants.NAME_GCC.equals(att.getName()) && (! type.hasAttribute(att))) { if (! annotated) { type = type.annotate(); annotated = true; } type.addAttribute(att); } } } // If the template is a wrapped type, continue with the wrapped type. template = template.isWrapped() ? template.toWrapped().getType() : null; } while (null != template); return type; } // ========================================================================= /** * Convert the specified type to an rvalue. If the specified type * has a shape, this method returns the resolved type, annotated * with any qualifiers and constant value. Otherwise, it returns * the specified type. * * @param type The type. * @return The type as an rvalue. */ public Type toRValue(Type type) { if (! type.hasShape()) return type; Type result = type.hasEnum() ? type.toEnum() : type.resolve(); if (hasQualifiers(type) || type.hasConstant()) { result = qualify(result.annotate(), type); if (type.hasConstant()) { result = result.constant(type.getConstant().getValue()); } } return result; } // ========================================================================= /** The memoized thread-local flag. */ private Boolean threadlocal = null; /** * Determine whether the current target supports thread-local * storage. * * @return <code>true</code> if the current target supports * thread-local storage. */ public boolean hasThreadLocals() { if (null == threadlocal) { threadlocal = Limits.IS_ELF ? Boolean.TRUE : Boolean.FALSE; } return threadlocal; } // ========================================================================= /** * Get the specified type's alignment in bytes. * * @param type The type. * @return The type's alignment. * @throws IllegalArgumentException Signals that the type does not * have a static alignment. */ public long getAlignment(Type type) { return getAlignment(type, true); } /** * Get the specified type's alignment in bytes. A type's natural * alignment is the type's alignment outside arrays, structures, and * unions. * * @param type The type. * @param natural The flag for determining the natural alignment. * @return The type's alignment. * @throws IllegalArgumentException * Signals that the type does not have a static alignment. */ public long getAlignment(Type type, boolean natural) { final Type resolved = type.resolve(); if (resolved.isStruct() || resolved.isUnion()) { if (isPacked(type)) { // If the structure or union is packed, its alignment is 1. return 1; } else { // Otherwise, the alignment is the maximum of the overall // alignment and the members' alignments. long alignment = Math.max(1, getAligned(type)); for (Type t : type.toTagged().getMembers()) { // Bit-fields with a zero width do not count. if (0 != t.toVariable().getWidth()) { alignment = Math.max(alignment, getAlignment(t, false)); } } return alignment; } } final long alignment = getAligned(type); if (-1 != alignment) return alignment; switch (type.tag()) { case VOID: return Limits.VOID_ALIGN; case BOOLEAN: return natural ? Limits.BOOL_NAT_ALIGN : Limits.BOOL_ALIGN; case INTEGER: case FLOAT: switch (type.resolve().toNumber().getKind()) { case CHAR: case S_CHAR: case U_CHAR: return 1; case SHORT: case U_SHORT: return natural ? Limits.SHORT_NAT_ALIGN : Limits.SHORT_ALIGN; case INT: case S_INT: case U_INT: return natural ? Limits.INT_NAT_ALIGN : Limits.INT_ALIGN; case LONG: case U_LONG: return natural ? Limits.LONG_NAT_ALIGN : Limits.LONG_ALIGN; case LONG_LONG: case U_LONG_LONG: return natural ? Limits.LONG_LONG_NAT_ALIGN : Limits.LONG_LONG_ALIGN; case FLOAT: return natural ? Limits.FLOAT_NAT_ALIGN : Limits.FLOAT_ALIGN; case DOUBLE: return natural ? Limits.DOUBLE_NAT_ALIGN : Limits.DOUBLE_ALIGN; case LONG_DOUBLE: return natural ? Limits.LONG_DOUBLE_NAT_ALIGN : Limits.LONG_DOUBLE_ALIGN; case FLOAT_COMPLEX: return natural ? Limits.FLOAT_NAT_ALIGN : Limits.FLOAT_ALIGN; case DOUBLE_COMPLEX: return natural ? Limits.DOUBLE_NAT_ALIGN : Limits.DOUBLE_ALIGN; case LONG_DOUBLE_COMPLEX: return natural ? Limits.LONG_DOUBLE_NAT_ALIGN : Limits.LONG_DOUBLE_ALIGN; default: throw new AssertionError("Invalid number kind " + type.toNumber().getKind()); } case POINTER: return natural ? Limits.POINTER_NAT_ALIGN : Limits.POINTER_ALIGN; case ARRAY: return getAlignment(type.resolve().toArray().getType(), natural); case FUNCTION: return Limits.FUNCTION_ALIGN; default: throw new IllegalArgumentException("Type without alignment " + type); } } // ========================================================================= /** * Get the specified type's size in bytes. * * @param type The type. * @return The type's size. * @throws IllegalArgumentException Signals that the type does not * have a static size. */ public long getSize(Type type) { switch (type.tag()) { case VOID: return Limits.VOID_SIZE; case BOOLEAN: return Limits.BOOL_SIZE; case INTEGER: case FLOAT: switch (type.resolve().toNumber().getKind()) { case CHAR: case S_CHAR: case U_CHAR: return 1; case SHORT: case U_SHORT: return Limits.SHORT_SIZE; case INT: case S_INT: case U_INT: return Limits.INT_SIZE; case LONG: case U_LONG: return Limits.LONG_SIZE; case LONG_LONG: case U_LONG_LONG: return Limits.LONG_LONG_SIZE; case FLOAT: return Limits.FLOAT_SIZE; case DOUBLE: return Limits.DOUBLE_SIZE; case LONG_DOUBLE: return Limits.LONG_DOUBLE_SIZE; case FLOAT_COMPLEX: return 2 * Limits.FLOAT_SIZE; case DOUBLE_COMPLEX: return 2 * Limits.DOUBLE_SIZE; case LONG_DOUBLE_COMPLEX: return 2 * Limits.LONG_DOUBLE_SIZE; default: throw new AssertionError("Invalid number kind " + type.toNumber().getKind()); } case POINTER: return Limits.POINTER_SIZE; case ARRAY: { ArrayT array = type.resolve().toArray(); if (array.hasLength()) { return getSize(array.getType()) * array.getLength(); } else { throw new IllegalArgumentException("Array without size"); } } case STRUCT: return layout(type.resolve().toStruct(), null); case UNION: { long size = 0; for (Type t : type.toTagged().getMembers()) { size = Math.max(size, getSize(t)); } return size; } case FUNCTION: return Limits.FUNCTION_SIZE; default: throw new IllegalArgumentException("Type without size " + type); } } // ========================================================================= /** * Get the specified member's offset for the specified structure or * union. * * @param type The structure or union. * @param name The member's name. * @return The offset or -1 if the structure or union does not have * a member with the specified name or the member is a bit-field. */ public long getOffset(StructOrUnionT type, String name) { if (type.isStruct()) { return layout(type.toStruct(), name); } else { for (VariableT var : type.getMembers()) { if (var.hasName(name)) { return 0; } else if (! var.hasName() && ! var.hasWidth()) { final long offset = getOffset(var.toStructOrUnion(), name); if (-1 != offset) return offset; } } return -1; } } // ========================================================================= /** * Lay out the specified structure. * * @param type The structure. * @param name The member name or <code>null</code> for the entire * structure. * @return The offset/size or -1 if the structure does not have a * member with the specified name or the member is a bit-field. */ public long layout(StructT type, String name) { final List<VariableT> members = type.getMembers(); final int memberCount = members.size(); final boolean hasTrailing = hasTrailingArray(type); final boolean isPacked = isPacked(type); long size = 0; long bitCount = 0; // The number of bits we have seen so far. long bitSize = 0; // The size of the member we are filling with bits. long bitAlign = 1; // The alignment of the member we are filling with bits. long maxAlign = Math.max(1, getAligned(type)); for (int i=0; i<memberCount; i++) { // Ignore any trailing array. if (hasTrailing && i == memberCount-1) break; // Process the member. final VariableT var = members.get(i); // Determine whether this member is the last one affecting the // structure's size. final boolean isLastMember = i == memberCount-1 || (hasTrailing && i == memberCount-2); // Determine the member's alignment. final long varAlign = getAlignment(var, false); final long align = isPacked ? 1 : varAlign; if (0 != var.getWidth()) maxAlign = Math.max(align, maxAlign); if (! var.hasKind(VariableT.Kind.BITFIELD)) { // Process the regular member. final long mod = size % align; if (0 != mod) size += (align - mod); if (null != name) { if (var.hasName(name)) { return size; } else if (! var.hasName()) { final long offset = getOffset(var.toStructOrUnion(), name); if (-1 != offset) return size + offset; } } size += getSize(var); } else { // Process bit-field. final int width = var.getWidth(); assert -1 < width; if (isPacked && (GCC_LEGACY_LAYOUT || 0 != width)) { // In packed structures, a bit-field just adds the specified // number of bits to the structure. GCC 4.0 and earlier // ignore the member's alignment for zero width bit-fields. bitCount += width; if (isLastMember || -1 == members.get(i+1).getWidth()) { size += (bitCount / Limits.CHAR_BITS); if (0 != bitCount % Limits.CHAR_BITS) size++; bitCount = 0; bitSize = 0; bitAlign = 1; } } else if (0 == width) { size += (bitCount / Limits.CHAR_BITS); if (0 != bitCount % Limits.CHAR_BITS) size++; // Use this member's alignment for padding. final long mod = size % varAlign; if (0 != mod) size += (varAlign - mod); bitCount = 0; bitSize = 0; bitAlign = 1; } else { if (0 == bitSize) { bitCount = width; bitSize = getSize(var); bitAlign = varAlign; } else if (bitCount + width <= Limits.toWidth(bitSize)) { bitCount += width; } else { size += bitSize; // Use the original member's alignment for padding. final long mod = size % bitAlign; if (0 != mod) size += (bitAlign - mod); bitCount = width; bitSize = getSize(var); bitAlign = varAlign; } if (isLastMember || -1 == members.get(i+1).getWidth()) { size += (bitCount / Limits.CHAR_BITS); if (0 != bitCount % Limits.CHAR_BITS) size++; // Any necessary padding is either emitted when processing // the next non-bit-field member or when completing the // size calculation through the code below. bitCount = 0; bitSize = 0; bitAlign = 1; } } } } if (null != name) return -1; // Account for padding to comply with the maximum alignment. final long mod = size % maxAlign; if (0 != mod) size += (maxAlign - mod); return size; } /** The canonical GCC packed attribute. */ private static final Attribute PACKED = new Attribute(Constants.NAME_GCC, new Attribute("packed", null)); /** * Determine whether the specified type has a GCC packed attribute. * * @param type The type. * @return <code>true</code> if the type has a packed attribute. */ protected boolean isPacked(Type type) { return type.hasAttribute(PACKED); } /** * Get the specified type's alignment. If the specified type has a * GCC aligned attribute, this method returns the corresponding * alignment. Otherwise, it returns -1. * * @param type The type. * @return The alignment or -1 if the type does not have an aligned * attribute. */ protected long getAligned(Type type) { long alignment = -1; do { for (Attribute att : type.attributes()) { if (Constants.NAME_GCC.equals(att.getName())) { att = (Attribute)att.getValue(); if ("aligned".equals(att.getName())) { if (null == att.getValue()) { alignment = Math.max(Limits.LONG_LONG_ALIGN, Limits.LONG_DOUBLE_ALIGN); } else { alignment = ((BigInteger)att.getValue()).longValue(); } } } } if (type.isWrapped()) { type = type.toWrapped().getType(); } else { break; } } while (true); return alignment; } // ========================================================================= /** * Get the specified number's size in bits. * * @param number The number. * @return The size in bits. */ public long getWidth(Type number) { switch (number.tag()) { case BOOLEAN: case INTEGER: case FLOAT: return getSize(number) * Limits.CHAR_BITS; default: throw new AssertionError("Not a C number " + number); } } /** * Fit the specified number to the closest integer type, starting * with <code>int</code>. * * @param number The number. * @return The closest containing integer type or {@link * ErrorT#TYPE} if the number is too large. */ public Type fit(BigInteger number) { if (Limits.fitsInt(number)) { return NumberT.INT; } else if (Limits.fitsUnsignedInt(number)) { return NumberT.U_INT; } else if (Limits.fitsLong(number)) { return NumberT.LONG; } else if (Limits.fitsUnsignedLong(number)) { return NumberT.U_LONG; } else if (Limits.fitsLongLong(number)) { return NumberT.LONG_LONG; } else if (Limits.fitsUnsignedLongLong(number)) { return NumberT.U_LONG_LONG; } else { return ErrorT.TYPE; } } /** * Determine whether the specified number fits the specified integer * type. * * @param number The number. * @param type The integer type. * @return <code>true</code> if the number fits the type. */ public boolean fits(BigInteger number, Type type) { switch (type.tag()) { case BOOLEAN: return Limits.fitsUnsignedChar(number); case INTEGER: switch (type.resolve().toInteger().getKind()) { case CHAR: return Limits.IS_CHAR_SIGNED? Limits.fitsChar(number) : Limits.fitsUnsignedChar(number); case S_CHAR: return Limits.fitsChar(number); case U_CHAR: return Limits.fitsUnsignedChar(number); case SHORT: return Limits.fitsShort(number); case U_SHORT: return Limits.fitsUnsignedShort(number); case INT: case S_INT: return Limits.fitsInt(number); case U_INT: return Limits.fitsUnsignedInt(number); case LONG: return Limits.fitsLong(number); case U_LONG: return Limits.fitsUnsignedLong(number); case LONG_LONG: return Limits.fitsLongLong(number); case U_LONG_LONG: return Limits.fitsUnsignedLongLong(number); } default: throw new AssertionError("Not a C integer " + type); } } /** * Mask the specified number as a value of this integer type. * * @param number The number. * @param type The type. * @return The number masked as a value of this type. */ public BigInteger mask(BigInteger number, Type type) { switch (type.tag()) { case BOOLEAN: return (0 != number.signum()) ? BigInteger.ONE : BigInteger.ZERO; case INTEGER: switch (type.resolve().toInteger().getKind()) { case CHAR: return Limits.IS_CHAR_SIGNED ? Limits.maskAsSignedChar(number) : Limits.maskAsUnsignedChar(number); case S_CHAR: return Limits.maskAsSignedChar(number); case U_CHAR: return Limits.maskAsUnsignedChar(number); case SHORT: return Limits.maskAsShort(number); case U_SHORT: return Limits.maskAsUnsignedShort(number); case INT: case S_INT: return Limits.maskAsInt(number); case U_INT: return Limits.maskAsUnsignedInt(number); case LONG: return Limits.maskAsLong(number); case U_LONG: return Limits.maskAsUnsignedLong(number); case LONG_LONG: return Limits.maskAsLongLong(number); case U_LONG_LONG: return Limits.maskAsUnsignedLongLong(number); } default: throw new AssertionError("Not a C integer " + type); } } // ========================================================================= /** * Get the specified type's designation. * * @param type The type. * @return The designation. */ public String toDesignation(Type type) { switch (type.tag()) { case BOOLEAN: case INTEGER: case FLOAT: case POINTER: return "scalar"; case ARRAY: return "array"; case STRUCT: return "struct"; case UNION: return "union"; case FUNCTION: return "function"; case INTERNAL: return type.resolve().toInternal().getName(); default: throw new AssertionError("Not a C type " + type); } } // ========================================================================= /** * Integer promote the specified type. This method resolves the * type and, if the type is integral, then performs C's integer * promotion (C99 6.3.1.1). Additionally, it normalizes implicit * and signed int types to int types. * * @param type The type. * @return The integer promoted type. */ public Type promote(Type type) { // Flag for whether the type represents a bit-field and int is not // signed. boolean flip = ((! Limits.IS_INT_SIGNED) && type.hasVariable() && type.toVariable().hasWidth()); type = type.resolve(); switch (type.tag()) { case BOOLEAN: return NumberT.INT; case INTEGER: switch (type.toInteger().getKind()) { case CHAR: case S_CHAR: case U_CHAR: case SHORT: // Shorts and types of lesser rank always fit into an int. return NumberT.INT; case U_SHORT: if (Limits.SHORT_SIZE < Limits.INT_SIZE) { // Unsigned shorts fit into regular ints if their size is // smaller. return NumberT.INT; } else { return NumberT.U_INT; } case INT: return flip ? NumberT.U_INT : NumberT.INT; case S_INT: return NumberT.INT; // Normalize to INT. case U_INT: case LONG: case U_LONG: case LONG_LONG: case U_LONG_LONG: // Nothing to promote. return type; default: throw new AssertionError("Not a C integer " + type); } default: return type; } } /** * Argument promote this type. This method resolves the type and, * if the type is an integral type or a float, then performs C's * default argument promotions (C99 6.5.2.2). * * @param type The type. * @return The argument promoted type. */ public Type promoteArgument(Type type) { Type resolved = type.resolve(); if (resolved.isFloat()) { if (NumberT.Kind.FLOAT == resolved.toFloat().getKind()) { return NumberT.DOUBLE; } else { return resolved; } } else { return promote(type); } } /** * Pointerize the specified type. This method resolves the type * and, if the type is an array or function, then performs C's * pointer decay (C99 6.3.2.1). * * @param type The type. * @return The pointerized type. */ public Type pointerize(Type type) { type = type.resolve(); switch (type.tag()) { case ARRAY: return new PointerT(type.toArray().getType()); case FUNCTION: return new PointerT(type); default: return type; } } // ========================================================================= /** * Perform the usual arithmetic conversions. Per C99 6.3.1.8, this * method performs the usual arithmetic conversions for the * specified two types and returns the type of the corresponding * result. * * @param t1 The first type. * @param t2 The second type. * @return The converted type. * @throws IllegalArgumentException Signals that either type is * not arithmetic. */ public Type convert(Type t1, Type t2) { // We can't combine non-arithmetic types. if ((! isArithmetic(t1)) || t1.hasError()) { throw new IllegalArgumentException("Not an arithmetic type " + t1); } else if ((! isArithmetic(t2)) || t2.hasError()) { throw new IllegalArgumentException("Not an arithmetic type " + t2); } // Promote the types. Note that promotion turns enums, // enumerators, and bit-fields into integers. t1 = promote(t1); t2 = promote(t2); // Now, we are left with integers and floats. NumberT.Kind k1 = ((NumberT)t1).getKind(); NumberT.Kind k2 = ((NumberT)t2).getKind(); // First, we convert any complex types. if ((! isReal(t1)) || (! isReal(t2))) { if ((NumberT.Kind.LONG_DOUBLE_COMPLEX == k1) || (NumberT.Kind.LONG_DOUBLE_COMPLEX == k2)) { return NumberT.LONG_DOUBLE_COMPLEX; } else if ((NumberT.Kind.DOUBLE_COMPLEX == k1) || (NumberT.Kind.DOUBLE_COMPLEX == k2)) { return NumberT.DOUBLE_COMPLEX; } if ((NumberT.Kind.FLOAT_COMPLEX == k1) || (NumberT.Kind.FLOAT_COMPLEX == k2)) { return NumberT.FLOAT_COMPLEX; } } // Next, we convert any real types. if ((NumberT.Kind.LONG_DOUBLE == k1) || (NumberT.Kind.LONG_DOUBLE == k2)) { return NumberT.LONG_DOUBLE; } else if ((NumberT.Kind.DOUBLE == k1) || (NumberT.Kind.DOUBLE == k2)) { return NumberT.DOUBLE; } else if ((NumberT.Kind.FLOAT == k1) || (NumberT.Kind.FLOAT == k2)) { return NumberT.FLOAT; } // Otherwise, the integer promotions are performed... Well, they // have already been performed above and we are guaranteed to have // integers here. IntegerT i1 = t1.toInteger(); IntegerT i2 = t2.toInteger(); // If both operands have the same type, ... if (k1 == k2) return i1; // Otherwise,... if (i1.isSigned() == i2.isSigned()) { return (k1.ordinal() < k2.ordinal()) ? i2 : i1; } // Otherwise,... if (! i1.isSigned()) { if (k1.ordinal() > k2.ordinal()) { return i1; } } else { if (k2.ordinal() > k1.ordinal()) { return i2; } } // Otherwise,... if (i1.isSigned()) { if (getSize(i1) > getSize(i2)) { return i1; } } else { if (getSize(i2) > getSize(i1)) { return i2; } } // Otherwise,... if (i1.isSigned()) { if (NumberT.Kind.INT == k1) { return NumberT.U_INT; } else if (NumberT.Kind.LONG == k1) { return NumberT.U_LONG; } else { return NumberT.U_LONG_LONG; } } else { if (NumberT.Kind.INT == k2) { return NumberT.U_INT; } else if (NumberT.Kind.LONG == k2) { return NumberT.U_LONG; } else { return NumberT.U_LONG_LONG; } } } // ========================================================================= /** * Compose the specified types. This method determines whether the * two types are compatible while also constructing a composite type * as specified in C99 6.2.7. If the types are compatible, the * resulting type is wrapped exactly as the first type. If the * types are not compatible, the resulting type is {@link * ErrorT#TYPE}. * * <p />Note that if both types are derived types, this method * ensures that any referenced types have the same qualifiers. * However, it does not ensure that the two types have the same * qualifiers. As a result, two types <code>t1</code> and * <code>t2</code> are compatible if: * <pre> * C.hasSameQualfiers(t1, t2) && (! C.compose(t1, t2).isError()) * </pre> * * <p />Further note that the composed type does not preserve any * annotations or wraped types and thus needs to be annotated with * the two type's qualifiers etc. * * @see #equal(Type,Type) * * @param t1 The first type. * @param t2 The second type. * @param pedantic The flag for pedantic composition. * @return The composed type. */ public Type compose(Type t1, Type t2, boolean pedantic) { return compose(t1, t2, pedantic, true); } /** * Compose the specified types. * * @param t1 The first type. * @param t2 The second type. * @param pedantic The flag for pedantic composition. * @param recursive The flag for recursive invocations. * @return The composed type. */ protected Type compose(Type t1, Type t2, boolean pedantic, boolean recursive) { if (recursive) { // Preserve any wrapped types. if (t1.isEnum()) { return t1.equals(t2) ? t1 : ErrorT.TYPE; } else if (t1.isWrapped()) { Type w1 = t1.toWrapped().getType(); Type c = compose(w1, t2, pedantic, true); if (c.isError()) { return ErrorT.TYPE; } else if (w1 == c) { return t1; } else { switch (t1.wtag()) { case ALIAS: return new AliasT(t1, t1.toAlias().getName(), c); case ANNOTATED: return new AnnotatedT(t1, c); case ENUMERATOR: { EnumeratorT e = t1.toEnumerator(); return new EnumeratorT(t1, c, e.getName(), e.getValue()); } case VARIABLE: { VariableT v = t1.toVariable(); return v.hasWidth() ? new VariableT(t1, c, v.getName(), v.getWidth()) : new VariableT(t1, c, v.getKind(), v.getName()); } default: throw new AssertionError("Invalid type " + t1); } } } } else { // Unwrap t1 while still checking enums. while (t1.isWrapped()) { if (t1.isEnum()) { return t1.equals(t2) ? t1 : ErrorT.TYPE; } else { t1 = t1.toWrapped().getType(); } } } // t1 has already been resolved; resolve t2 as well. t2 = t2.resolve(); // Make sure both types have the same tag. if (t1 == t2) return t1; if (t1.tag() != t2.tag()) return ErrorT.TYPE; // Now, do the type-specific composition. switch (t1.tag()) { case ERROR: return ErrorT.TYPE; case VOID: case BOOLEAN: return t1; case FLOAT: case INTEGER: return NumberT.equal(t1.toNumber().getKind(), t2.toNumber().getKind()) ? t1 : ErrorT.TYPE; case INTERNAL: return t1.toInternal().getName().equals(t2.toInternal().getName()) ? t1 : ErrorT.TYPE; case LABEL: return t1.toLabel().getName().equals(t2.toLabel().getName()) ? t1 : ErrorT.TYPE; case STRUCT: case UNION: return t1 == t2 ? t1 : ErrorT.TYPE; case POINTER: { // C99 6.7.2, 6.7.5.1 Type pt1 = t1.toPointer().getType(); Type pt2 = t2.toPointer().getType(); if (! hasSameQualifiers(pt1, pt2)) return ErrorT.TYPE; Type ptc = compose(pt1, pt2, pedantic, true); if (ptc.isError()) return ErrorT.TYPE; return pt1 == ptc ? t1 : new PointerT(t1, ptc); } case ARRAY: return composeArrays(t1.toArray(), t2.toArray()); case FUNCTION: return composeFunctions(t1.toFunction(), t2.toFunction(), pedantic); default: throw new AssertionError("Not a C type " + t1); } } /** * Compose the specified array types (C99 6.2.7). * * @param a1 The first array. * @param a2 The second array. * @return The composed type. */ protected Type composeArrays(ArrayT a1, ArrayT a2) { if (! hasSameQualifiers(a1.getType(), a2.getType())) return ErrorT.TYPE; Type el = compose(a1.getType(), a2.getType(), true); if (el.isError()) return ErrorT.TYPE; if (a1.isVarLength()) { if (el == a1.getType()) { return a1; } else { return new ArrayT(a1, el, a1.isVarLength(), a1.getLength()); } } else if (a2.isVarLength()) { if (el == a2.getType()) { return a2; } else { return new ArrayT(a2, el, a2.isVarLength(), a2.getLength()); } } if (a1.hasLength() && a2.hasLength()) { if (a1.getLength() == a2.getLength()) { if (el == a1.getType()) { return a1; } else { return new ArrayT(a1, el, a1.isVarLength(), a1.getLength()); } } else { return ErrorT.TYPE; } } if (a1.hasLength()) { if (el == a1.getType()) { return a1; } else { return new ArrayT(a1, el, a1.isVarLength(), a1.getLength()); } } if (a2.hasLength()) { if (el == a1.getType()) { return a2; } else { return new ArrayT(a2, el, a2.isVarLength(), a2.getLength()); } } return el == a1.getType() ? a1 : new ArrayT(a1, el, a1.isVarLength(), a1.getLength()); } /** * Compose the specified function types (C99 6.2.7). Note that this * method ignores any exceptions, which are not part of the C * language anyway. * * @param f1 The first function. * @param f2 The second function. * @param pedantic The flag for pedantic composition. * @return The composed type. */ protected Type composeFunctions(FunctionT f1, FunctionT f2, boolean pedantic) { // Compare the names. if (null == f1.getName()) { if (null != f2.getName()) return ErrorT.TYPE; } else { if (! f1.getName().equals(f2.getName())) return ErrorT.TYPE; } // The flag for whether the component types differ from this type. boolean differs = false; // Compare the results. if (! hasSameQualifiers(f1.getResult(), f2.getResult())) return ErrorT.TYPE; final Type res = compose(f1.getResult(), f2.getResult(), true); if (res.isError()) return ErrorT.TYPE; if (f1.getResult() != res) differs = true; // Process functions with old-style declarations, since we ignore // their parameters. if (f1.hasAttribute(Constants.ATT_STYLE_OLD)) { if (f2.hasAttribute(Constants.ATT_STYLE_OLD)) { // Both types are functions without a parameter type list. // However, if type information from the function definition // is available, we preserve (but not check) it. if (f1.hasAttribute(Constants.ATT_DEFINED) || (! f2.hasAttribute(Constants.ATT_DEFINED))) { return differs ? new FunctionT(f1, res, f1.getParameters(), f1.isVarArgs()) : f1; } else { return new FunctionT(f2, res, f2.getParameters(), f2.isVarArgs()); } } else { // The other type is a function type with a parameter type // list. It is compatible with this function type as long as // it is not variable and this function type is declared. if (f2.isVarArgs() && (! f1.hasAttribute(Constants.ATT_DEFINED))) { return ErrorT.TYPE; } else { return new FunctionT(f2, res, f2.getParameters(), f2.isVarArgs()); } } } else if (f2.hasAttribute(Constants.ATT_STYLE_OLD)) { // This type is a function type with a parameter type list. It // is compatible with the other function type as long as it is // not variable and the other function type is declared. if (f1.isVarArgs() && (! f2.hasAttribute(Constants.ATT_DEFINED))) { return ErrorT.TYPE; } else { return differs ? new FunctionT(f1, res, f1.getParameters(), f1.isVarArgs()) : f1; } } // Neither type is a function type with an old-style declaration. // Continue by checking the parameters. if (f1.getParameters().size() != f2.getParameters().size()) { return ErrorT.TYPE; } if (f1.isVarArgs() != f2.isVarArgs()) return ErrorT.TYPE; final int size = f1.getParameters().size(); List<Type> par = differs ? new ArrayList<Type>(f1.getParameters()) : null; for (int i=0; i<size; i++) { final Type p1 = f1.getParameters().get(i); final Type p2 = f2.getParameters().get(i); if (pedantic && ! hasSameQualifiers(p1, p2)) return ErrorT.TYPE; final Type p3 = compose(p1, p2, true); if (p3.isError()) return ErrorT.TYPE; if (p1 != p3) { if (null == par) par = new ArrayList<Type>(f1.getParameters()); differs = true; par.set(i, p3); } } // Ignore the exceptions and we are done. if (! differs) return f1; if (null == par) par = new ArrayList<Type>(f1.getParameters()); final FunctionT result = new FunctionT(f1, res, par, f1.isVarArgs()); return result; } /** * Determine whether the specified types are equal to each other. * Calling this method on types <code>t1</code> and <code>t2</code> * is equivalent to: * <pre> * C.hasSameQualifiers(t1, t2) && (! C.compose(t1, t2).isError()) * </pre> * * @param t1 The first type. * @param t2 The second type. * @return <code>true</code> if the types are equal. */ public boolean equal(Type t1, Type t2) { return hasSameQualifiers(t1, t2) && (! compose(t1, t2, true).isError()); } // ========================================================================= /** The factor for chars. */ protected final BigInteger FACTOR_CHAR = BigInteger.valueOf(2).pow(Limits.CHAR_BITS); /** The factor for wide chars. */ protected final BigInteger FACTOR_WIDE = BigInteger.valueOf(2).pow(Limits.CHAR_BITS * Limits.WCHAR_SIZE); /** * Type the specified C character literal. This method determines * the type for the specified character literal, which may be a wide * character literal, and returns that type. The type is annotated * with the literal's constant value, even if the value does not fit * the type. * * @param literal The literal. * @return The corresponding constant valued type. */ public Type typeCharacter(String literal) { // The flag for a wide character literal. boolean isWide = false; // Strip wide marker and ticks. if (literal.startsWith("L")) { literal = literal.substring(2, literal.length()-1); isWide = true; } else { literal = literal.substring(1, literal.length()-1); } // Unescape. literal = Utilities.unescape(literal); // Determine the value. BigInteger value = BigInteger.ZERO; BigInteger factor = isWide ? FACTOR_WIDE : FACTOR_CHAR; final int length = literal.length(); for (int i=0; i<length; i++) { value = value.multiply(factor). add(BigInteger.valueOf(literal.charAt(i))); } // Determine the type, which according to C99 6.4.4.4 is an int // for chars. return isWide ? WCHAR.annotate().constant(value) : NumberT.CHAR.annotate().constant(value); } /** * Type the specified integer literal. This method returns the type * for the specified C integer literal wrapped in the literal's * constant value. If the specified literal does not fit any type, * this method returns the largest appropriate type. * * @param literal The literal. * @return The corresponding type and constant value. */ public Type typeInteger(String literal) { // Extract the suffix. boolean isUnsigned = false; boolean isLong = false; boolean isLongLong = false; int idx = literal.length(); for ( ; idx>0; idx--) { char c = literal.charAt(idx-1); if (('u' == c) || ('U' == c)) { isUnsigned = true; } else if (('l' == c) || ('L' == c)) { if (isLong) { isLong = false; isLongLong = true; } else { isLong = true; } } else { break; } } literal = literal.substring(0, idx); // Extract the radix. int radix = 10; if (literal.startsWith("0x") || literal.startsWith("0X")) { radix = 16; literal = literal.substring(2); } else if (literal.startsWith("0")) { radix = 8; } // Extract the value. final BigInteger value = new BigInteger(literal, radix); // Find a fitting type (C99 6.4.4.1) IntegerT type = null; if (isUnsigned) { if (isLongLong) { if (Limits.fitsUnsignedLongLong(value)) { type = NumberT.U_LONG_LONG; } } else if (isLong) { if (Limits.fitsUnsignedLong(value)) { type = NumberT.U_LONG; } else if (Limits.fitsUnsignedLongLong(value)) { type = NumberT.U_LONG_LONG; } } else { if (Limits.fitsUnsignedInt(value)) { type = NumberT.U_INT; } else if (Limits.fitsUnsignedLong(value)) { type = NumberT.U_LONG; } else if (Limits.fitsUnsignedLongLong(value)) { type = NumberT.U_LONG_LONG; } } // Patch in the biggest type. if (null == type) type = NumberT.U_LONG_LONG; } else if (10 == radix) { if (isLongLong) { if (Limits.fitsLongLong(value)) { type = NumberT.LONG_LONG; } } else if (isLong) { if (Limits.fitsLong(value)) { type = NumberT.LONG; } else if (Limits.fitsLongLong(value)) { type = NumberT.LONG_LONG; } } else { if (Limits.fitsInt(value)) { type = NumberT.INT; } else if (Limits.fitsLong(value)) { type = NumberT.LONG; } else if (Limits.fitsLongLong(value)) { type = NumberT.LONG_LONG; } } // Patch in the biggest type. if (null == type) type = NumberT.LONG_LONG; } else { if (isLongLong) { if (Limits.fitsLongLong(value)) { type = NumberT.LONG_LONG; } else if (Limits.fitsUnsignedLongLong(value)) { type = NumberT.U_LONG_LONG; } } else if (isLong) { if (Limits.fitsLong(value)) { type = NumberT.LONG; } else if (Limits.fitsUnsignedLong(value)) { type = NumberT.U_LONG; } else if (Limits.fitsLongLong(value)) { type = NumberT.LONG_LONG; } else if (Limits.fitsUnsignedLongLong(value)) { type = NumberT.U_LONG_LONG; } } else { if (Limits.fitsInt(value)) { type = NumberT.INT; } else if (Limits.fitsUnsignedInt(value)) { type = NumberT.U_INT; } else if (Limits.fitsLong(value)) { type = NumberT.LONG; } else if (Limits.fitsUnsignedLong(value)) { type = NumberT.U_LONG; } else if (Limits.fitsLongLong(value)) { type = NumberT.LONG_LONG; } else if (Limits.fitsUnsignedLongLong(value)) { type = NumberT.U_LONG_LONG; } } // Patch in the biggest type. if (null == type) type = NumberT.U_LONG_LONG; } // Done. return type.annotate().constant(value); } /** * Type the specified floating point literal. This method returns * the type for the specified C floating point literal wrapped in * the literal's constant value. * * @param literal The literal. * @return The corresponding type. */ public Type typeFloat(String literal) { char suffix = literal.charAt(literal.length()-1); boolean chop = false; FloatT type; switch (suffix) { case 'f': case 'F': chop = true; type = NumberT.FLOAT; break; case 'l': case 'L': chop = true; type = NumberT.LONG_DOUBLE; break; case 'd': case 'D': chop = true; // Fall through. default: type = NumberT.DOUBLE; } if (chop) literal = literal.substring(0, literal.length()-1); return type.annotate().constant(Double.valueOf(literal)); } }