2006-10-14 15:06:20 +08:00
|
|
|
//===--- TargetInfo.cpp - Information about Target machine ----------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
2007-12-30 03:59:25 +08:00
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
2006-10-14 15:06:20 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file implements the TargetInfo and TargetInfoImpl interfaces.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/Basic/TargetInfo.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/Basic/AddressSpaces.h"
|
2013-02-09 06:30:41 +08:00
|
|
|
#include "clang/Basic/CharInfo.h"
|
2009-11-06 04:14:16 +08:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2007-09-23 02:29:59 +08:00
|
|
|
#include "llvm/ADT/APFloat.h"
|
2007-11-25 08:25:21 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2011-09-23 13:35:21 +08:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2008-04-06 12:02:29 +08:00
|
|
|
#include <cstdlib>
|
2006-10-14 15:06:20 +08:00
|
|
|
using namespace clang;
|
|
|
|
|
2011-03-19 06:38:29 +08:00
|
|
|
static const LangAS::Map DefaultAddrSpaceMap = { 0 };
|
|
|
|
|
2008-03-08 16:59:43 +08:00
|
|
|
// TargetInfo Constructor.
|
2013-06-30 00:37:14 +08:00
|
|
|
TargetInfo::TargetInfo(const llvm::Triple &T) : TargetOpts(), Triple(T) {
|
2010-04-15 14:18:39 +08:00
|
|
|
// Set defaults. Defaults are set for a 32-bit RISC platform, like PPC or
|
|
|
|
// SPARC. These should be overridden by concrete targets as needed.
|
2011-12-22 11:51:45 +08:00
|
|
|
BigEndian = true;
|
2009-04-20 05:38:35 +08:00
|
|
|
TLSSupported = true;
|
2010-04-24 00:29:58 +08:00
|
|
|
NoAsmVariants = false;
|
2008-05-09 14:08:39 +08:00
|
|
|
PointerWidth = PointerAlign = 32;
|
2011-01-06 16:27:10 +08:00
|
|
|
BoolWidth = BoolAlign = 8;
|
2008-05-08 13:58:21 +08:00
|
|
|
IntWidth = IntAlign = 32;
|
2008-05-09 13:50:02 +08:00
|
|
|
LongWidth = LongAlign = 32;
|
|
|
|
LongLongWidth = LongLongAlign = 64;
|
2011-12-17 06:34:14 +08:00
|
|
|
SuitableAlign = 64;
|
Implement target-specific __attribute__((aligned)) value
The GCC construct __attribute__((aligned)) is defined to set alignment
to "the default alignment for the target architecture" according to
the GCC documentation:
The default alignment is sufficient for all scalar types, but may not be
enough for all vector types on a target that supports vector operations.
The default alignment is fixed for a particular target ABI.
clang currently hard-coded an alignment of 16 bytes for that construct,
which is correct on some platforms (including X86), but wrong on others
(including SystemZ). Since this value is ABI-relevant, it is important
to get correct for compatibility purposes.
This patch adds a new TargetInfo member "DefaultAlignForAttributeAligned"
that targets can set to the appropriate default __attribute__((aligned))
value.
Note that I'm deliberately *not* using the existing "SuitableAlign"
value, which is used to set the pre-defined macro __BIGGEST_ALIGNMENT__,
since those two values may not be the same on all platforms. In fact,
on X86, __attribute__((aligned)) always uses 16-byte alignment, while
__BIGGEST_ALIGNMENT__ may be larger if AVX-2 or AVX-512 are supported.
(This is actually not yet correctly implemented in clang either.)
The patch provides a value for DefaultAlignForAttributeAligned only for
SystemZ, and leaves the default for all other targets at 16, which means
no visible change in behavior on all other targets. (The value is still
wrong for some other targets, but I'd prefer to leave it to the target
maintainers for those platforms to fix.)
llvm-svn: 235397
2015-04-22 01:29:35 +08:00
|
|
|
DefaultAlignForAttributeAligned = 128;
|
2013-05-07 00:23:57 +08:00
|
|
|
MinGlobalAlign = 0;
|
2011-10-15 07:23:15 +08:00
|
|
|
HalfWidth = 16;
|
|
|
|
HalfAlign = 16;
|
2008-05-20 22:21:01 +08:00
|
|
|
FloatWidth = 32;
|
|
|
|
FloatAlign = 32;
|
2008-04-19 01:17:24 +08:00
|
|
|
DoubleWidth = 64;
|
2008-05-20 22:21:01 +08:00
|
|
|
DoubleAlign = 64;
|
|
|
|
LongDoubleWidth = 64;
|
|
|
|
LongDoubleAlign = 64;
|
2010-06-05 07:15:27 +08:00
|
|
|
LargeArrayMinWidth = 0;
|
|
|
|
LargeArrayAlign = 0;
|
2011-10-15 04:59:01 +08:00
|
|
|
MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 0;
|
2012-07-14 07:57:43 +08:00
|
|
|
MaxVectorAlign = 0;
|
2015-07-15 04:52:32 +08:00
|
|
|
MaxTLSAlign = 0;
|
2015-07-02 11:40:19 +08:00
|
|
|
SimdDefaultAlign = 0;
|
2008-11-01 00:05:19 +08:00
|
|
|
SizeType = UnsignedLong;
|
2008-11-02 10:43:55 +08:00
|
|
|
PtrDiffType = SignedLong;
|
2008-10-31 17:52:39 +08:00
|
|
|
IntMaxType = SignedLongLong;
|
2009-02-14 06:28:55 +08:00
|
|
|
IntPtrType = SignedLong;
|
2008-11-02 10:43:55 +08:00
|
|
|
WCharType = SignedInt;
|
2009-10-21 12:59:34 +08:00
|
|
|
WIntType = SignedInt;
|
2009-07-14 14:30:34 +08:00
|
|
|
Char16Type = UnsignedShort;
|
|
|
|
Char32Type = UnsignedInt;
|
2009-07-01 11:36:11 +08:00
|
|
|
Int64Type = SignedLongLong;
|
2009-11-21 08:49:54 +08:00
|
|
|
SigAtomicType = SignedInt;
|
2012-11-27 10:58:24 +08:00
|
|
|
ProcessIDType = SignedInt;
|
2012-04-17 05:03:30 +08:00
|
|
|
UseSignedCharForObjCBool = true;
|
2010-04-15 23:06:18 +08:00
|
|
|
UseBitFieldTypeAlignment = true;
|
2011-08-04 09:21:14 +08:00
|
|
|
UseZeroLengthBitfieldAlignment = false;
|
|
|
|
ZeroLengthBitfieldBoundary = 0;
|
2011-10-15 07:23:15 +08:00
|
|
|
HalfFormat = &llvm::APFloat::IEEEhalf;
|
2008-03-08 16:59:43 +08:00
|
|
|
FloatFormat = &llvm::APFloat::IEEEsingle;
|
|
|
|
DoubleFormat = &llvm::APFloat::IEEEdouble;
|
|
|
|
LongDoubleFormat = &llvm::APFloat::IEEEdouble;
|
2015-08-06 07:48:05 +08:00
|
|
|
DataLayoutString = nullptr;
|
2008-10-06 03:22:37 +08:00
|
|
|
UserLabelPrefix = "_";
|
2011-02-11 00:52:03 +08:00
|
|
|
MCountName = "mcount";
|
2011-09-08 22:20:25 +08:00
|
|
|
RegParmMax = 0;
|
|
|
|
SSERegParmMax = 0;
|
2010-05-27 08:35:16 +08:00
|
|
|
HasAlignMac68kSupport = false;
|
2015-09-18 04:55:33 +08:00
|
|
|
HasBuiltinMSVaList = false;
|
2010-07-15 07:39:36 +08:00
|
|
|
|
|
|
|
// Default to no types using fpret.
|
|
|
|
RealTypeUsesObjCFPRet = 0;
|
2010-08-22 06:46:04 +08:00
|
|
|
|
2011-11-01 00:27:11 +08:00
|
|
|
// Default to not using fp2ret for __Complex long double
|
|
|
|
ComplexLongDoubleUsesFP2Ret = false;
|
|
|
|
|
2014-01-15 03:35:09 +08:00
|
|
|
// Set the C++ ABI based on the triple.
|
2014-03-28 06:50:18 +08:00
|
|
|
TheCXXABI.set(Triple.isKnownWindowsMSVCEnvironment()
|
2014-01-15 03:35:09 +08:00
|
|
|
? TargetCXXABI::Microsoft
|
|
|
|
: TargetCXXABI::GenericItanium);
|
2011-03-19 06:38:29 +08:00
|
|
|
|
|
|
|
// Default to an empty address space map.
|
|
|
|
AddrSpaceMap = &DefaultAddrSpaceMap;
|
2013-09-13 20:04:22 +08:00
|
|
|
UseAddrSpaceMapMangling = false;
|
Implement a new 'availability' attribute, that allows one to specify
which versions of an OS provide a certain facility. For example,
void foo()
__attribute__((availability(macosx,introduced=10.2,deprecated=10.4,obsoleted=10.6)));
says that the function "foo" was introduced in 10.2, deprecated in
10.4, and completely obsoleted in 10.6. This attribute ties in with
the deployment targets (e.g., -mmacosx-version-min=10.1 specifies that
we want to deploy back to Mac OS X 10.1). There are several concrete
behaviors that this attribute enables, as illustrated with the
function foo() above:
- If we choose a deployment target >= Mac OS X 10.4, uses of "foo"
will result in a deprecation warning, as if we had placed
attribute((deprecated)) on it (but with a better diagnostic)
- If we choose a deployment target >= Mac OS X 10.6, uses of "foo"
will result in an "unavailable" warning (in C)/error (in C++), as
if we had placed attribute((unavailable)) on it
- If we choose a deployment target prior to 10.2, foo() is
weak-imported (if it is a kind of entity that can be weak
imported), as if we had placed the weak_import attribute on it.
Naturally, there can be multiple availability attributes on a
declaration, for different platforms; only the current platform
matters when checking availability attributes.
The only platforms this attribute currently works for are "ios" and
"macosx", since we already have -mxxxx-version-min flags for them and we
have experience there with macro tricks translating down to the
deprecated/unavailable/weak_import attributes. The end goal is to open
this up to other platforms, and even extension to other "platforms"
that are really libraries (say, through a #pragma clang
define_system), but that hasn't yet been designed and we may want to
shake out more issues with this narrower problem first.
Addresses <rdar://problem/6690412>.
As a drive-by bug-fix, if an entity is both deprecated and
unavailable, we only emit the "unavailable" diagnostic.
llvm-svn: 128127
2011-03-23 08:50:03 +08:00
|
|
|
|
|
|
|
// Default to an unknown platform name.
|
|
|
|
PlatformName = "unknown";
|
|
|
|
PlatformMinVersion = VersionTuple();
|
2007-09-23 02:29:59 +08:00
|
|
|
}
|
|
|
|
|
2008-03-08 16:59:43 +08:00
|
|
|
// Out of line virtual dtor for TargetInfo.
|
|
|
|
TargetInfo::~TargetInfo() {}
|
2007-09-23 02:29:59 +08:00
|
|
|
|
2009-02-06 13:04:11 +08:00
|
|
|
/// getTypeName - Return the user string for the specified integer type enum.
|
|
|
|
/// For example, SignedShort -> "short".
|
|
|
|
const char *TargetInfo::getTypeName(IntType T) {
|
|
|
|
switch (T) {
|
2011-09-23 13:06:16 +08:00
|
|
|
default: llvm_unreachable("not an integer!");
|
2014-07-29 05:06:22 +08:00
|
|
|
case SignedChar: return "signed char";
|
2013-09-05 19:23:21 +08:00
|
|
|
case UnsignedChar: return "unsigned char";
|
2009-02-06 13:04:11 +08:00
|
|
|
case SignedShort: return "short";
|
|
|
|
case UnsignedShort: return "unsigned short";
|
|
|
|
case SignedInt: return "int";
|
|
|
|
case UnsignedInt: return "unsigned int";
|
|
|
|
case SignedLong: return "long int";
|
|
|
|
case UnsignedLong: return "long unsigned int";
|
|
|
|
case SignedLongLong: return "long long int";
|
|
|
|
case UnsignedLongLong: return "long long unsigned int";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-21 14:24:21 +08:00
|
|
|
/// getTypeConstantSuffix - Return the constant suffix for the specified
|
|
|
|
/// integer type enum. For example, SignedLong -> "L".
|
2014-07-18 04:12:32 +08:00
|
|
|
const char *TargetInfo::getTypeConstantSuffix(IntType T) const {
|
2009-10-21 14:24:21 +08:00
|
|
|
switch (T) {
|
2011-09-23 13:06:16 +08:00
|
|
|
default: llvm_unreachable("not an integer!");
|
2013-09-05 19:23:21 +08:00
|
|
|
case SignedChar:
|
2009-10-21 14:24:21 +08:00
|
|
|
case SignedShort:
|
|
|
|
case SignedInt: return "";
|
|
|
|
case SignedLong: return "L";
|
|
|
|
case SignedLongLong: return "LL";
|
2013-09-05 19:23:21 +08:00
|
|
|
case UnsignedChar:
|
2014-07-18 04:12:32 +08:00
|
|
|
if (getCharWidth() < getIntWidth())
|
|
|
|
return "";
|
2009-10-21 14:24:21 +08:00
|
|
|
case UnsignedShort:
|
2014-07-18 04:12:32 +08:00
|
|
|
if (getShortWidth() < getIntWidth())
|
|
|
|
return "";
|
2009-10-21 14:24:21 +08:00
|
|
|
case UnsignedInt: return "U";
|
|
|
|
case UnsignedLong: return "UL";
|
|
|
|
case UnsignedLongLong: return "ULL";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-15 19:30:00 +08:00
|
|
|
/// getTypeFormatModifier - Return the printf format modifier for the
|
|
|
|
/// specified integer type enum. For example, SignedLong -> "l".
|
|
|
|
|
|
|
|
const char *TargetInfo::getTypeFormatModifier(IntType T) {
|
|
|
|
switch (T) {
|
|
|
|
default: llvm_unreachable("not an integer!");
|
|
|
|
case SignedChar:
|
|
|
|
case UnsignedChar: return "hh";
|
|
|
|
case SignedShort:
|
|
|
|
case UnsignedShort: return "h";
|
|
|
|
case SignedInt:
|
|
|
|
case UnsignedInt: return "";
|
|
|
|
case SignedLong:
|
|
|
|
case UnsignedLong: return "l";
|
|
|
|
case SignedLongLong:
|
|
|
|
case UnsignedLongLong: return "ll";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-18 15:10:59 +08:00
|
|
|
/// getTypeWidth - Return the width (in bits) of the specified integer type
|
2009-10-21 14:24:21 +08:00
|
|
|
/// enum. For example, SignedInt -> getIntWidth().
|
|
|
|
unsigned TargetInfo::getTypeWidth(IntType T) const {
|
|
|
|
switch (T) {
|
2011-09-23 13:06:16 +08:00
|
|
|
default: llvm_unreachable("not an integer!");
|
2013-09-05 19:23:21 +08:00
|
|
|
case SignedChar:
|
|
|
|
case UnsignedChar: return getCharWidth();
|
2009-11-06 05:21:32 +08:00
|
|
|
case SignedShort:
|
2009-10-21 14:24:21 +08:00
|
|
|
case UnsignedShort: return getShortWidth();
|
2009-11-06 05:21:32 +08:00
|
|
|
case SignedInt:
|
2009-10-21 14:24:21 +08:00
|
|
|
case UnsignedInt: return getIntWidth();
|
2009-11-06 05:21:32 +08:00
|
|
|
case SignedLong:
|
2009-10-21 14:24:21 +08:00
|
|
|
case UnsignedLong: return getLongWidth();
|
2009-11-06 05:21:32 +08:00
|
|
|
case SignedLongLong:
|
2009-10-21 14:24:21 +08:00
|
|
|
case UnsignedLongLong: return getLongLongWidth();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2013-09-05 19:23:21 +08:00
|
|
|
TargetInfo::IntType TargetInfo::getIntTypeByWidth(
|
|
|
|
unsigned BitWidth, bool IsSigned) const {
|
|
|
|
if (getCharWidth() == BitWidth)
|
|
|
|
return IsSigned ? SignedChar : UnsignedChar;
|
|
|
|
if (getShortWidth() == BitWidth)
|
|
|
|
return IsSigned ? SignedShort : UnsignedShort;
|
|
|
|
if (getIntWidth() == BitWidth)
|
|
|
|
return IsSigned ? SignedInt : UnsignedInt;
|
|
|
|
if (getLongWidth() == BitWidth)
|
|
|
|
return IsSigned ? SignedLong : UnsignedLong;
|
|
|
|
if (getLongLongWidth() == BitWidth)
|
|
|
|
return IsSigned ? SignedLongLong : UnsignedLongLong;
|
|
|
|
return NoInt;
|
|
|
|
}
|
|
|
|
|
2014-06-25 09:31:33 +08:00
|
|
|
TargetInfo::IntType TargetInfo::getLeastIntTypeByWidth(unsigned BitWidth,
|
|
|
|
bool IsSigned) const {
|
|
|
|
if (getCharWidth() >= BitWidth)
|
|
|
|
return IsSigned ? SignedChar : UnsignedChar;
|
|
|
|
if (getShortWidth() >= BitWidth)
|
|
|
|
return IsSigned ? SignedShort : UnsignedShort;
|
|
|
|
if (getIntWidth() >= BitWidth)
|
|
|
|
return IsSigned ? SignedInt : UnsignedInt;
|
|
|
|
if (getLongWidth() >= BitWidth)
|
|
|
|
return IsSigned ? SignedLong : UnsignedLong;
|
|
|
|
if (getLongLongWidth() >= BitWidth)
|
|
|
|
return IsSigned ? SignedLongLong : UnsignedLongLong;
|
|
|
|
return NoInt;
|
|
|
|
}
|
|
|
|
|
2013-09-05 19:23:21 +08:00
|
|
|
TargetInfo::RealType TargetInfo::getRealTypeByWidth(unsigned BitWidth) const {
|
|
|
|
if (getFloatWidth() == BitWidth)
|
|
|
|
return Float;
|
|
|
|
if (getDoubleWidth() == BitWidth)
|
|
|
|
return Double;
|
|
|
|
|
|
|
|
switch (BitWidth) {
|
|
|
|
case 96:
|
|
|
|
if (&getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended)
|
|
|
|
return LongDouble;
|
|
|
|
break;
|
|
|
|
case 128:
|
2013-09-09 15:46:54 +08:00
|
|
|
if (&getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble ||
|
|
|
|
&getLongDoubleFormat() == &llvm::APFloat::IEEEquad)
|
2013-09-05 19:23:21 +08:00
|
|
|
return LongDouble;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NoFloat;
|
|
|
|
}
|
|
|
|
|
2010-10-18 15:10:59 +08:00
|
|
|
/// getTypeAlign - Return the alignment (in bits) of the specified integer type
|
2009-11-06 05:21:32 +08:00
|
|
|
/// enum. For example, SignedInt -> getIntAlign().
|
|
|
|
unsigned TargetInfo::getTypeAlign(IntType T) const {
|
|
|
|
switch (T) {
|
2011-09-23 13:06:16 +08:00
|
|
|
default: llvm_unreachable("not an integer!");
|
2013-09-05 19:23:21 +08:00
|
|
|
case SignedChar:
|
|
|
|
case UnsignedChar: return getCharAlign();
|
2009-11-06 05:21:32 +08:00
|
|
|
case SignedShort:
|
|
|
|
case UnsignedShort: return getShortAlign();
|
|
|
|
case SignedInt:
|
|
|
|
case UnsignedInt: return getIntAlign();
|
|
|
|
case SignedLong:
|
|
|
|
case UnsignedLong: return getLongAlign();
|
|
|
|
case SignedLongLong:
|
|
|
|
case UnsignedLongLong: return getLongLongAlign();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2009-10-26 06:49:18 +08:00
|
|
|
/// isTypeSigned - Return whether an integer types is signed. Returns true if
|
2009-10-21 14:24:21 +08:00
|
|
|
/// the type is signed; false otherwise.
|
2010-12-26 07:25:43 +08:00
|
|
|
bool TargetInfo::isTypeSigned(IntType T) {
|
2009-10-21 14:24:21 +08:00
|
|
|
switch (T) {
|
2011-09-23 13:06:16 +08:00
|
|
|
default: llvm_unreachable("not an integer!");
|
2013-09-05 19:23:21 +08:00
|
|
|
case SignedChar:
|
2009-10-21 14:24:21 +08:00
|
|
|
case SignedShort:
|
|
|
|
case SignedInt:
|
|
|
|
case SignedLong:
|
2010-10-18 15:10:59 +08:00
|
|
|
case SignedLongLong:
|
2009-10-21 14:24:21 +08:00
|
|
|
return true;
|
2013-09-05 19:23:21 +08:00
|
|
|
case UnsignedChar:
|
2009-10-21 14:24:21 +08:00
|
|
|
case UnsignedShort:
|
|
|
|
case UnsignedInt:
|
|
|
|
case UnsignedLong:
|
2010-10-18 15:10:59 +08:00
|
|
|
case UnsignedLongLong:
|
2009-10-21 14:24:21 +08:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2014-07-06 13:14:24 +08:00
|
|
|
/// adjust - Set forced language options.
|
2009-11-06 04:14:16 +08:00
|
|
|
/// Apply changes to the target information with respect to certain
|
|
|
|
/// language options which change the target configuration.
|
2014-07-06 13:14:24 +08:00
|
|
|
void TargetInfo::adjust(const LangOptions &Opts) {
|
2010-04-15 23:06:22 +08:00
|
|
|
if (Opts.NoBitFieldTypeAlign)
|
|
|
|
UseBitFieldTypeAlignment = false;
|
|
|
|
if (Opts.ShortWChar)
|
2009-11-06 04:14:16 +08:00
|
|
|
WCharType = UnsignedShort;
|
2013-09-09 17:17:24 +08:00
|
|
|
|
|
|
|
if (Opts.OpenCL) {
|
|
|
|
// OpenCL C requires specific widths for types, irrespective of
|
|
|
|
// what these normally are for the target.
|
|
|
|
// We also define long long and long double here, although the
|
|
|
|
// OpenCL standard only mentions these as "reserved".
|
|
|
|
IntWidth = IntAlign = 32;
|
|
|
|
LongWidth = LongAlign = 64;
|
|
|
|
LongLongWidth = LongLongAlign = 128;
|
|
|
|
HalfWidth = HalfAlign = 16;
|
|
|
|
FloatWidth = FloatAlign = 32;
|
2013-12-19 02:15:03 +08:00
|
|
|
|
|
|
|
// Embedded 32-bit targets (OpenCL EP) might have double C type
|
|
|
|
// defined as float. Let's not override this as it might lead
|
|
|
|
// to generating illegal code that uses 64bit doubles.
|
|
|
|
if (DoubleWidth != FloatWidth) {
|
|
|
|
DoubleWidth = DoubleAlign = 64;
|
|
|
|
DoubleFormat = &llvm::APFloat::IEEEdouble;
|
|
|
|
}
|
2013-09-09 17:17:24 +08:00
|
|
|
LongDoubleWidth = LongDoubleAlign = 128;
|
|
|
|
|
|
|
|
assert(PointerWidth == 32 || PointerWidth == 64);
|
2013-09-10 16:00:34 +08:00
|
|
|
bool Is32BitArch = PointerWidth == 32;
|
|
|
|
SizeType = Is32BitArch ? UnsignedInt : UnsignedLong;
|
|
|
|
PtrDiffType = Is32BitArch ? SignedInt : SignedLong;
|
|
|
|
IntPtrType = Is32BitArch ? SignedInt : SignedLong;
|
2013-09-09 17:17:24 +08:00
|
|
|
|
|
|
|
IntMaxType = SignedLongLong;
|
|
|
|
Int64Type = SignedLong;
|
|
|
|
|
|
|
|
HalfFormat = &llvm::APFloat::IEEEhalf;
|
|
|
|
FloatFormat = &llvm::APFloat::IEEEsingle;
|
|
|
|
LongDoubleFormat = &llvm::APFloat::IEEEquad;
|
|
|
|
}
|
2009-11-06 04:14:16 +08:00
|
|
|
}
|
2009-10-21 14:24:21 +08:00
|
|
|
|
2015-09-02 02:13:20 +08:00
|
|
|
bool TargetInfo::initFeatureMap(llvm::StringMap<bool> &Features,
|
|
|
|
DiagnosticsEngine &Diags, StringRef CPU,
|
|
|
|
std::vector<std::string> &FeatureVec) const {
|
|
|
|
for (const auto &F : FeatureVec) {
|
|
|
|
const char *Name = F.c_str();
|
|
|
|
// Apply the feature via the target.
|
|
|
|
bool Enabled = Name[0] == '+';
|
|
|
|
setFeatureEnabled(Features, Name + 1, Enabled);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-09-23 02:29:59 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2007-01-29 13:24:35 +08:00
|
|
|
|
2011-07-23 18:55:15 +08:00
|
|
|
static StringRef removeGCCRegisterPrefix(StringRef Name) {
|
2008-02-06 08:11:32 +08:00
|
|
|
if (Name[0] == '%' || Name[0] == '#')
|
2010-01-31 03:12:25 +08:00
|
|
|
Name = Name.substr(1);
|
2010-10-18 15:10:59 +08:00
|
|
|
|
2010-01-31 03:12:25 +08:00
|
|
|
return Name;
|
2008-02-06 08:11:32 +08:00
|
|
|
}
|
|
|
|
|
2011-06-29 02:20:53 +08:00
|
|
|
/// isValidClobber - Returns whether the passed in string is
|
|
|
|
/// a valid clobber in an inline asm statement. This is used by
|
|
|
|
/// Sema.
|
2011-07-23 18:55:15 +08:00
|
|
|
bool TargetInfo::isValidClobber(StringRef Name) const {
|
2011-06-29 02:20:53 +08:00
|
|
|
return (isValidGCCRegisterName(Name) ||
|
|
|
|
Name == "memory" || Name == "cc");
|
|
|
|
}
|
|
|
|
|
2007-11-25 07:38:12 +08:00
|
|
|
/// isValidGCCRegisterName - Returns whether the passed in string
|
|
|
|
/// is a valid register name according to GCC. This is used by Sema for
|
|
|
|
/// inline asm statements.
|
2011-07-23 18:55:15 +08:00
|
|
|
bool TargetInfo::isValidGCCRegisterName(StringRef Name) const {
|
2010-01-31 03:12:25 +08:00
|
|
|
if (Name.empty())
|
|
|
|
return false;
|
2010-10-18 15:10:59 +08:00
|
|
|
|
2007-11-25 08:25:21 +08:00
|
|
|
const char * const *Names;
|
|
|
|
unsigned NumNames;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-25 08:25:21 +08:00
|
|
|
// Get rid of any register prefix.
|
2010-01-31 03:12:25 +08:00
|
|
|
Name = removeGCCRegisterPrefix(Name);
|
2014-08-17 21:19:48 +08:00
|
|
|
if (Name.empty())
|
|
|
|
return false;
|
2008-02-06 08:11:32 +08:00
|
|
|
|
2008-03-08 16:24:01 +08:00
|
|
|
getGCCRegNames(Names, NumNames);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-25 08:25:21 +08:00
|
|
|
// If we have a number it maps to an entry in the register name array.
|
2013-02-09 06:30:41 +08:00
|
|
|
if (isDigit(Name[0])) {
|
2010-01-31 03:12:25 +08:00
|
|
|
int n;
|
|
|
|
if (!Name.getAsInteger(0, n))
|
2007-11-25 08:25:21 +08:00
|
|
|
return n >= 0 && (unsigned)n < NumNames;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check register names.
|
|
|
|
for (unsigned i = 0; i < NumNames; i++) {
|
2010-01-31 03:12:25 +08:00
|
|
|
if (Name == Names[i])
|
2007-11-25 08:25:21 +08:00
|
|
|
return true;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-06-21 08:05:20 +08:00
|
|
|
// Check any additional names that we have.
|
|
|
|
const AddlRegName *AddlNames;
|
|
|
|
unsigned NumAddlNames;
|
|
|
|
getGCCAddlRegNames(AddlNames, NumAddlNames);
|
|
|
|
for (unsigned i = 0; i < NumAddlNames; i++)
|
|
|
|
for (unsigned j = 0; j < llvm::array_lengthof(AddlNames[i].Names); j++) {
|
|
|
|
if (!AddlNames[i].Names[j])
|
|
|
|
break;
|
|
|
|
// Make sure the register that the additional name is for is within
|
|
|
|
// the bounds of the register names from above.
|
|
|
|
if (AddlNames[i].Names[j] == Name && AddlNames[i].RegNum < NumNames)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-11-25 08:25:21 +08:00
|
|
|
// Now check aliases.
|
2008-03-08 16:24:01 +08:00
|
|
|
const GCCRegAlias *Aliases;
|
2007-11-25 08:25:21 +08:00
|
|
|
unsigned NumAliases;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-08 16:24:01 +08:00
|
|
|
getGCCRegAliases(Aliases, NumAliases);
|
2007-11-25 08:25:21 +08:00
|
|
|
for (unsigned i = 0; i < NumAliases; i++) {
|
|
|
|
for (unsigned j = 0 ; j < llvm::array_lengthof(Aliases[i].Aliases); j++) {
|
|
|
|
if (!Aliases[i].Aliases[j])
|
|
|
|
break;
|
2010-01-31 03:12:25 +08:00
|
|
|
if (Aliases[i].Aliases[j] == Name)
|
2007-11-25 08:25:21 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-25 07:38:12 +08:00
|
|
|
return false;
|
|
|
|
}
|
2007-11-27 12:11:28 +08:00
|
|
|
|
2011-07-23 18:55:15 +08:00
|
|
|
StringRef
|
|
|
|
TargetInfo::getNormalizedGCCRegisterName(StringRef Name) const {
|
2007-11-27 12:11:28 +08:00
|
|
|
assert(isValidGCCRegisterName(Name) && "Invalid register passed in");
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-01-31 03:12:25 +08:00
|
|
|
// Get rid of any register prefix.
|
|
|
|
Name = removeGCCRegisterPrefix(Name);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-27 12:11:28 +08:00
|
|
|
const char * const *Names;
|
|
|
|
unsigned NumNames;
|
|
|
|
|
2008-03-08 16:24:01 +08:00
|
|
|
getGCCRegNames(Names, NumNames);
|
2007-11-27 12:11:28 +08:00
|
|
|
|
|
|
|
// First, check if we have a number.
|
2013-02-09 06:30:41 +08:00
|
|
|
if (isDigit(Name[0])) {
|
2010-01-31 03:12:25 +08:00
|
|
|
int n;
|
|
|
|
if (!Name.getAsInteger(0, n)) {
|
2009-09-09 23:08:12 +08:00
|
|
|
assert(n >= 0 && (unsigned)n < NumNames &&
|
2007-11-27 12:11:28 +08:00
|
|
|
"Out of bounds register number!");
|
|
|
|
return Names[n];
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-06-21 08:05:20 +08:00
|
|
|
// Check any additional names that we have.
|
|
|
|
const AddlRegName *AddlNames;
|
|
|
|
unsigned NumAddlNames;
|
|
|
|
getGCCAddlRegNames(AddlNames, NumAddlNames);
|
|
|
|
for (unsigned i = 0; i < NumAddlNames; i++)
|
|
|
|
for (unsigned j = 0; j < llvm::array_lengthof(AddlNames[i].Names); j++) {
|
|
|
|
if (!AddlNames[i].Names[j])
|
|
|
|
break;
|
|
|
|
// Make sure the register that the additional name is for is within
|
|
|
|
// the bounds of the register names from above.
|
|
|
|
if (AddlNames[i].Names[j] == Name && AddlNames[i].RegNum < NumNames)
|
|
|
|
return Name;
|
|
|
|
}
|
|
|
|
|
2007-11-27 12:11:28 +08:00
|
|
|
// Now check aliases.
|
2008-03-08 16:24:01 +08:00
|
|
|
const GCCRegAlias *Aliases;
|
2007-11-27 12:11:28 +08:00
|
|
|
unsigned NumAliases;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-03-08 16:24:01 +08:00
|
|
|
getGCCRegAliases(Aliases, NumAliases);
|
2007-11-27 12:11:28 +08:00
|
|
|
for (unsigned i = 0; i < NumAliases; i++) {
|
|
|
|
for (unsigned j = 0 ; j < llvm::array_lengthof(Aliases[i].Aliases); j++) {
|
|
|
|
if (!Aliases[i].Aliases[j])
|
|
|
|
break;
|
2010-01-31 03:12:25 +08:00
|
|
|
if (Aliases[i].Aliases[j] == Name)
|
2007-11-27 12:11:28 +08:00
|
|
|
return Aliases[i].Register;
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-27 12:11:28 +08:00
|
|
|
return Name;
|
|
|
|
}
|
|
|
|
|
2009-04-27 01:19:08 +08:00
|
|
|
bool TargetInfo::validateOutputConstraint(ConstraintInfo &Info) const {
|
|
|
|
const char *Name = Info.getConstraintStr().c_str();
|
2007-11-27 12:11:28 +08:00
|
|
|
// An output constraint must start with '=' or '+'
|
|
|
|
if (*Name != '=' && *Name != '+')
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (*Name == '+')
|
2009-04-26 15:16:29 +08:00
|
|
|
Info.setIsReadWrite();
|
2007-11-27 12:11:28 +08:00
|
|
|
|
|
|
|
Name++;
|
|
|
|
while (*Name) {
|
|
|
|
switch (*Name) {
|
|
|
|
default:
|
2009-04-26 15:16:29 +08:00
|
|
|
if (!validateAsmConstraint(Name, Info)) {
|
2008-04-25 00:36:38 +08:00
|
|
|
// FIXME: We temporarily return false
|
2007-11-27 12:11:28 +08:00
|
|
|
// so we can add more constraints as we hit it.
|
|
|
|
// Eventually, an unknown constraint should just be treated as 'g'.
|
2008-04-25 00:36:38 +08:00
|
|
|
return false;
|
2007-11-27 12:11:28 +08:00
|
|
|
}
|
2015-01-10 18:43:19 +08:00
|
|
|
break;
|
2007-11-27 12:11:28 +08:00
|
|
|
case '&': // early clobber.
|
2015-01-10 18:43:19 +08:00
|
|
|
Info.setEarlyClobber();
|
2007-11-27 12:11:28 +08:00
|
|
|
break;
|
2009-10-13 12:32:07 +08:00
|
|
|
case '%': // commutative.
|
|
|
|
// FIXME: Check that there is a another register after this one.
|
|
|
|
break;
|
2007-11-27 12:11:28 +08:00
|
|
|
case 'r': // general register.
|
2009-04-26 15:16:29 +08:00
|
|
|
Info.setAllowsRegister();
|
2007-11-27 12:11:28 +08:00
|
|
|
break;
|
|
|
|
case 'm': // memory operand.
|
2010-09-08 02:40:41 +08:00
|
|
|
case 'o': // offsetable memory operand.
|
|
|
|
case 'V': // non-offsetable memory operand.
|
|
|
|
case '<': // autodecrement memory operand.
|
|
|
|
case '>': // autoincrement memory operand.
|
2009-04-26 15:16:29 +08:00
|
|
|
Info.setAllowsMemory();
|
2007-11-27 12:11:28 +08:00
|
|
|
break;
|
|
|
|
case 'g': // general register, memory operand or immediate integer.
|
2009-01-18 10:12:04 +08:00
|
|
|
case 'X': // any operand.
|
2009-04-26 15:16:29 +08:00
|
|
|
Info.setAllowsRegister();
|
|
|
|
Info.setAllowsMemory();
|
2007-11-27 12:11:28 +08:00
|
|
|
break;
|
2010-08-11 03:20:14 +08:00
|
|
|
case ',': // multiple alternative constraint. Pass it.
|
2010-08-11 08:58:20 +08:00
|
|
|
// Handle additional optional '=' or '+' modifiers.
|
2010-09-18 09:15:13 +08:00
|
|
|
if (Name[1] == '=' || Name[1] == '+')
|
2010-08-11 08:58:20 +08:00
|
|
|
Name++;
|
2010-08-11 03:20:14 +08:00
|
|
|
break;
|
2015-01-11 16:52:38 +08:00
|
|
|
case '#': // Ignore as constraint.
|
|
|
|
while (Name[1] && Name[1] != ',')
|
|
|
|
Name++;
|
2015-01-11 17:39:03 +08:00
|
|
|
break;
|
2010-08-11 03:20:14 +08:00
|
|
|
case '?': // Disparage slightly code.
|
2010-09-08 02:40:41 +08:00
|
|
|
case '!': // Disparage severely.
|
2012-10-29 20:20:54 +08:00
|
|
|
case '*': // Ignore for choosing register preferences.
|
2010-08-11 03:20:14 +08:00
|
|
|
break; // Pass them.
|
2007-11-27 12:11:28 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-27 12:11:28 +08:00
|
|
|
Name++;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2015-01-10 18:43:19 +08:00
|
|
|
// Early clobber with a read-write constraint which doesn't permit registers
|
|
|
|
// is invalid.
|
|
|
|
if (Info.earlyClobber() && Info.isReadWrite() && !Info.allowsRegister())
|
|
|
|
return false;
|
|
|
|
|
2013-04-19 06:49:48 +08:00
|
|
|
// If a constraint allows neither memory nor register operands it contains
|
|
|
|
// only modifiers. Reject it.
|
2013-04-18 21:23:23 +08:00
|
|
|
return Info.allowsMemory() || Info.allowsRegister();
|
2007-11-27 12:11:28 +08:00
|
|
|
}
|
|
|
|
|
2009-01-18 09:56:57 +08:00
|
|
|
bool TargetInfo::resolveSymbolicName(const char *&Name,
|
2009-04-27 01:57:12 +08:00
|
|
|
ConstraintInfo *OutputConstraints,
|
|
|
|
unsigned NumOutputs,
|
2009-04-26 15:16:29 +08:00
|
|
|
unsigned &Index) const {
|
2009-01-18 09:56:57 +08:00
|
|
|
assert(*Name == '[' && "Symbolic name did not start with '['");
|
|
|
|
Name++;
|
|
|
|
const char *Start = Name;
|
|
|
|
while (*Name && *Name != ']')
|
|
|
|
Name++;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-18 09:56:57 +08:00
|
|
|
if (!*Name) {
|
|
|
|
// Missing ']'
|
|
|
|
return false;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-01-18 09:56:57 +08:00
|
|
|
std::string SymbolicName(Start, Name - Start);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-27 01:57:12 +08:00
|
|
|
for (Index = 0; Index != NumOutputs; ++Index)
|
|
|
|
if (SymbolicName == OutputConstraints[Index].getName())
|
2009-01-18 09:56:57 +08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-04-27 01:57:12 +08:00
|
|
|
bool TargetInfo::validateInputConstraint(ConstraintInfo *OutputConstraints,
|
|
|
|
unsigned NumOutputs,
|
2009-04-26 15:16:29 +08:00
|
|
|
ConstraintInfo &Info) const {
|
2009-04-27 01:19:08 +08:00
|
|
|
const char *Name = Info.ConstraintStr.c_str();
|
|
|
|
|
2013-12-16 11:20:06 +08:00
|
|
|
if (!*Name)
|
|
|
|
return false;
|
|
|
|
|
2007-11-27 12:11:28 +08:00
|
|
|
while (*Name) {
|
|
|
|
switch (*Name) {
|
|
|
|
default:
|
|
|
|
// Check if we have a matching constraint
|
|
|
|
if (*Name >= '0' && *Name <= '9') {
|
2015-01-11 18:22:41 +08:00
|
|
|
const char *DigitStart = Name;
|
|
|
|
while (Name[1] >= '0' && Name[1] <= '9')
|
|
|
|
Name++;
|
|
|
|
const char *DigitEnd = Name;
|
|
|
|
unsigned i;
|
|
|
|
if (StringRef(DigitStart, DigitEnd - DigitStart + 1)
|
|
|
|
.getAsInteger(10, i))
|
|
|
|
return false;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-27 12:11:28 +08:00
|
|
|
// Check if matching constraint is out of bounds.
|
2015-01-11 18:22:41 +08:00
|
|
|
if (i >= NumOutputs) return false;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-11-03 10:22:29 +08:00
|
|
|
// A number must refer to an output only operand.
|
|
|
|
if (OutputConstraints[i].isReadWrite())
|
|
|
|
return false;
|
|
|
|
|
2010-11-03 10:54:51 +08:00
|
|
|
// If the constraint is already tied, it must be tied to the
|
|
|
|
// same operand referenced to by the number.
|
|
|
|
if (Info.hasTiedOperand() && Info.getTiedOperand() != i)
|
|
|
|
return false;
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
// The constraint should have the same info as the respective
|
2009-01-28 04:38:24 +08:00
|
|
|
// output constraint.
|
2009-04-27 02:05:25 +08:00
|
|
|
Info.setTiedOperand(i, OutputConstraints[i]);
|
2009-04-26 15:16:29 +08:00
|
|
|
} else if (!validateAsmConstraint(Name, Info)) {
|
2008-08-25 17:46:27 +08:00
|
|
|
// FIXME: This error return is in place temporarily so we can
|
|
|
|
// add more constraints as we hit it. Eventually, an unknown
|
|
|
|
// constraint should just be treated as 'g'.
|
|
|
|
return false;
|
2009-01-18 09:56:57 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '[': {
|
|
|
|
unsigned Index = 0;
|
2009-04-27 01:57:12 +08:00
|
|
|
if (!resolveSymbolicName(Name, OutputConstraints, NumOutputs, Index))
|
2009-01-18 09:56:57 +08:00
|
|
|
return false;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-11-03 10:54:51 +08:00
|
|
|
// If the constraint is already tied, it must be tied to the
|
|
|
|
// same operand referenced to by the number.
|
|
|
|
if (Info.hasTiedOperand() && Info.getTiedOperand() != Index)
|
|
|
|
return false;
|
|
|
|
|
2015-01-11 17:57:13 +08:00
|
|
|
// A number must refer to an output only operand.
|
|
|
|
if (OutputConstraints[Index].isReadWrite())
|
|
|
|
return false;
|
|
|
|
|
2010-08-12 07:03:37 +08:00
|
|
|
Info.setTiedOperand(Index, OutputConstraints[Index]);
|
2009-01-18 09:56:57 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2007-12-09 03:32:57 +08:00
|
|
|
case '%': // commutative
|
|
|
|
// FIXME: Fail if % is used with the last operand.
|
|
|
|
break;
|
2007-11-27 12:11:28 +08:00
|
|
|
case 'i': // immediate integer.
|
2008-03-09 14:02:02 +08:00
|
|
|
case 'n': // immediate integer with a known value.
|
2007-11-27 12:11:28 +08:00
|
|
|
break;
|
2009-05-06 12:33:31 +08:00
|
|
|
case 'I': // Various constant constraints with target-specific meanings.
|
|
|
|
case 'J':
|
|
|
|
case 'K':
|
|
|
|
case 'L':
|
|
|
|
case 'M':
|
|
|
|
case 'N':
|
|
|
|
case 'O':
|
|
|
|
case 'P':
|
2015-01-06 12:26:34 +08:00
|
|
|
if (!validateAsmConstraint(Name, Info))
|
|
|
|
return false;
|
2009-05-06 12:33:31 +08:00
|
|
|
break;
|
2007-11-27 12:11:28 +08:00
|
|
|
case 'r': // general register.
|
2009-04-26 15:16:29 +08:00
|
|
|
Info.setAllowsRegister();
|
2007-11-27 12:11:28 +08:00
|
|
|
break;
|
|
|
|
case 'm': // memory operand.
|
2010-09-08 02:40:41 +08:00
|
|
|
case 'o': // offsettable memory operand.
|
|
|
|
case 'V': // non-offsettable memory operand.
|
|
|
|
case '<': // autodecrement memory operand.
|
|
|
|
case '>': // autoincrement memory operand.
|
2009-04-26 15:16:29 +08:00
|
|
|
Info.setAllowsMemory();
|
2007-11-27 12:11:28 +08:00
|
|
|
break;
|
|
|
|
case 'g': // general register, memory operand or immediate integer.
|
2009-01-18 10:12:04 +08:00
|
|
|
case 'X': // any operand.
|
2009-04-26 15:16:29 +08:00
|
|
|
Info.setAllowsRegister();
|
|
|
|
Info.setAllowsMemory();
|
2007-11-27 12:11:28 +08:00
|
|
|
break;
|
2010-09-22 06:04:54 +08:00
|
|
|
case 'E': // immediate floating point.
|
|
|
|
case 'F': // immediate floating point.
|
|
|
|
case 'p': // address operand.
|
|
|
|
break;
|
2010-08-11 03:20:14 +08:00
|
|
|
case ',': // multiple alternative constraint. Ignore comma.
|
|
|
|
break;
|
2015-01-11 17:39:03 +08:00
|
|
|
case '#': // Ignore as constraint.
|
|
|
|
while (Name[1] && Name[1] != ',')
|
|
|
|
Name++;
|
|
|
|
break;
|
2010-08-11 03:20:14 +08:00
|
|
|
case '?': // Disparage slightly code.
|
2011-04-15 13:22:18 +08:00
|
|
|
case '!': // Disparage severely.
|
2012-10-29 20:20:54 +08:00
|
|
|
case '*': // Ignore for choosing register preferences.
|
2010-08-11 03:20:14 +08:00
|
|
|
break; // Pass them.
|
2007-11-27 12:11:28 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-27 12:11:28 +08:00
|
|
|
Name++;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2007-11-27 12:11:28 +08:00
|
|
|
return true;
|
|
|
|
}
|