TargetInfo.h revision 5f016e2cb5d11daeb237544de1c5d59f20fe1a6e
15f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer//===--- TargetInfo.h - Expose information about the target -----*- C++ -*-===// 25f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 35f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// The LLVM Compiler Infrastructure 45f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 55f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// This file was developed by Chris Lattner and is distributed under 65f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// the University of Illinois Open Source License. See LICENSE.TXT for details. 75f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 85f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer//===----------------------------------------------------------------------===// 95f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// This file defines the TargetInfo and TargetInfoImpl interfaces. 115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer//===----------------------------------------------------------------------===// 135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#ifndef LLVM_CLANG_BASIC_TARGETINFO_H 155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#define LLVM_CLANG_BASIC_TARGETINFO_H 165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "clang/Basic/SourceLocation.h" 185f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include <vector> 195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include <string> 205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 215f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencernamespace clang { 225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerclass TargetInfoImpl; 245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerclass Diagnostic; 255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencernamespace Builtin { struct Info; } 265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// TargetInfo - This class exposes information about the current target set. 285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// A target set consists of a primary target and zero or more secondary targets 295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// which are each represented by a TargetInfoImpl object. TargetInfo responds 305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// to various queries as though it were the primary target, but keeps track of, 315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// and warns about, the first query made of it that are contradictary among the 325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// targets it tracks. For example, if it contains a "PPC32" and "PPC64" 335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// target, it will warn the first time the size of the 'long' datatype is 345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// queried. 355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// 365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// Note that TargetInfo does not take ownership of the various targets or the 375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// diagnostic info, but does expect them to be alive for as long as it is. 385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// 395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerclass TargetInfo { 405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// Primary - This tracks the primary target in the target set. 415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer const TargetInfoImpl *PrimaryTarget; 435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// SecondaryTargets - This tracks the set of secondary targets. 455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer std::vector<const TargetInfoImpl*> SecondaryTargets; 475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// Diag - If non-null, this object is used to report the first use of 495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// non-portable functionality in the translation unit. 505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Diagnostic *Diag; 525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// NonPortable - This instance variable keeps track of whether or not the 545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// current translation unit is portable across the set of targets tracked. 555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer bool NonPortable; 565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// These are all caches for target values. 585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned WCharWidth; 595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerpublic: 615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer TargetInfo(const TargetInfoImpl *Primary, Diagnostic *D = 0) { 625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer PrimaryTarget = Primary; 635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Diag = D; 645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer NonPortable = false; 655f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Initialize Cache values to uncomputed. 675f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer WCharWidth = 0; 685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// isNonPortable - Return true if the current translation unit has used a 715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// target property that is non-portable across the secondary targets. 725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer bool isNonPortable() const { 735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return NonPortable; 745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// isPortable - Return true if this translation unit is portable across the 775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// secondary targets so far. 785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer bool isPortable() const { 795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return !NonPortable; 805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// AddSecondaryTarget - Add a secondary target to the target set. 835f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer void AddSecondaryTarget(const TargetInfoImpl *Secondary) { 845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer SecondaryTargets.push_back(Secondary); 855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 865f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer ///===---- Target property query methods --------------------------------===// 885f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 895f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// DiagnoseNonPortability - Emit a diagnostic indicating that the current 905f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// translation unit is non-portable due to a construct at the specified 915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// location. DiagKind indicates what went wrong. 925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer void DiagnoseNonPortability(SourceLocation Loc, unsigned DiagKind); 935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getTargetDefines - Appends the target-specific #define values for this 955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// target set to the specified buffer. 965f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer void getTargetDefines(std::vector<char> &DefineBuffer); 975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// isCharSigned - Return true if 'char' is 'signed char' or false if it is 995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// treated as 'unsigned char'. This is implementation defined according to 1005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// C99 6.2.5p15. In our implementation, this is target-specific. 1015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer bool isCharSigned(SourceLocation Loc) { 1025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // FIXME: implement correctly. 1035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return true; 1045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1055f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1065f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getPointerWidth - Return the width of pointers on this target, we 1075f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// currently assume one pointer type. 1085f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getPointerWidth(SourceLocation Loc) { 1095f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 32; // FIXME: implement correctly. 1105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getBoolWidth - Return the size of '_Bool' and C++ 'bool' for this target, 1135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// in bits. 1145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getBoolWidth(SourceLocation Loc) { 1155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 8; // FIXME: implement correctly: wrong for ppc32. 1165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1185f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getCharWidth - Return the size of 'char', 'signed char' and 1195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 'unsigned char' for this target, in bits. 1205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getCharWidth(SourceLocation Loc) { 1215f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 8; // FIXME: implement correctly. 1225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getShortWidth - Return the size of 'signed short' and 'unsigned short' for 1255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// this target, in bits. 1265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getShortWidth(SourceLocation Loc) { 1275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 16; // FIXME: implement correctly. 1285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getIntWidth - Return the size of 'signed int' and 'unsigned int' for this 1315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// target, in bits. 1325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getIntWidth(SourceLocation Loc) { 1335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 32; // FIXME: implement correctly. 1345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getLongWidth - Return the size of 'signed long' and 'unsigned long' for 1375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// this target, in bits. 1385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getLongWidth(SourceLocation Loc) { 1395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 32; // FIXME: implement correctly: wrong for ppc64/x86-64 1405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getLongLongWidth - Return the size of 'signed long long' and 1435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 'unsigned long long' for this target, in bits. 1445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getLongLongWidth(SourceLocation Loc) { 1455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 64; // FIXME: implement correctly. 1465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getWCharWidth - Return the size of wchar_t in bits. 1495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 1505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getWCharWidth(SourceLocation Loc) { 1515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (!WCharWidth) ComputeWCharWidth(Loc); 1525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return WCharWidth; 1535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getIntMaxTWidth - Return the size of intmax_t and uintmax_t for this 1565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// target, in bits. 1575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getIntMaxTWidth(SourceLocation Loc) { 1585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // FIXME: implement correctly. 1595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 64; 1605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getTargetBuiltins - Return information about target-specific builtins for 1635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// the current primary target, and info about which builtins are non-portable 1645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// across the current set of primary and secondary targets. 1655f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords, 1665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer std::vector<const char *> &NonPortableBuiltins) const; 1675f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerprivate: 1695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer void ComputeWCharWidth(SourceLocation Loc); 1705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer}; 1715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// TargetInfoImpl - This class is implemented for specific targets and is used 1765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// by the TargetInfo class. Target implementations should initialize instance 1775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// variables and implement various virtual methods if the default values are 1785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// not appropriate for the target. 1795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerclass TargetInfoImpl { 1805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerprotected: 1815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned WCharWidth; /// sizeof(wchar_t) in bits. Default value is 32. 1825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerpublic: 1835f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer TargetInfoImpl() : WCharWidth(32) {} 1845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer virtual ~TargetInfoImpl() {} 1855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1865f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getTargetDefines - Return a list of the target-specific #define values set 1875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// when compiling to this target. Each string should be of the form "X", 1885f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// which results in '#define X 1' or "X=Y" which results in "#define X Y" 1895f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer virtual void getTargetDefines(std::vector<std::string> &Defines) const = 0; 1905f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getWCharWidth - Return the size of wchar_t in bits. 1925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 1935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned getWCharWidth() const { return WCharWidth; } 1945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// getTargetBuiltins - Return information about target-specific builtins for 1965f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// the target. 1975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer virtual void getTargetBuiltins(const Builtin::Info *&Records, 1985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned &NumRecords) const { 1995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Records = 0; 2005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer NumRecords = 0; 2015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 2025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerprivate: 2035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer virtual void ANCHOR(); // out-of-line virtual method for class. 2045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer}; 2055f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2065f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} // end namespace clang 2075f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2085f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#endif 209