From 1eff70451fbb079c1d5b8f45ff8c8a2b8f74d7ba Mon Sep 17 00:00:00 2001 From: Duncan Sands Date: Mon, 10 Dec 2007 17:43:13 +0000 Subject: [PATCH] Fix PR1836: in the interpreter, read and write apints using the minimum possible number of bytes. For little endian targets run on little endian machines, apints are stored in memory from LSB to MSB as before. For big endian targets on big endian machines they are stored from MSB to LSB which wasn't always the case before (if the target and host endianness doesn't match values are stored according to the host's endianness). Doing this requires knowing the endianness of the host, which is determined when configuring - thanks go to Anton for this. Only having access to little endian machines I was unable to properly test the big endian part, which is also the most complicated... git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@44796 91177308-0d34-0410-b5e6-96231b3b80d8 --- autoconf/configure.ac | 5 +- configure | 70 ++++++++++-------- include/llvm/Config/config.h.in | 6 ++ include/llvm/Target/TargetData.h | 11 +++ lib/ExecutionEngine/ExecutionEngine.cpp | 71 ++++++++++++------- .../2007-12-10-APIntLoadStore.ll | 19 +++++ 6 files changed, 129 insertions(+), 53 deletions(-) create mode 100644 test/ExecutionEngine/2007-12-10-APIntLoadStore.ll diff --git a/autoconf/configure.ac b/autoconf/configure.ac index 979e3a89865..fe16b5073e4 100644 --- a/autoconf/configure.ac +++ b/autoconf/configure.ac @@ -227,7 +227,10 @@ dnl Define a substitution, ARCH, for the target architecture AC_SUBST(ARCH,$llvm_cv_target_arch) dnl Check for the endianness of the target -AC_C_BIGENDIAN(AC_SUBST([ENDIAN],[big]),AC_SUBST([ENDIAN],[little])) +AC_C_BIGENDIAN([AC_SUBST([ENDIAN],[big]), + AC_DEFINE([MSB_FIRST], [1], [Define if this target is big endian])], + [AC_SUBST([ENDIAN],[little]), + AC_DEFINE([LSB_FIRST], [1], [Define if this target is little endian])]) dnl Check for build platform executable suffix if we're crosscompiling if test "$cross_compiling" = yes; then diff --git a/configure b/configure index 4b65605d5b0..b1fa7c09f71 100755 --- a/configure +++ b/configure @@ -828,7 +828,6 @@ NOLINKALL LLVM_ON_UNIX LLVM_ON_WIN32 ARCH -ENDIAN CC CFLAGS LDFLAGS @@ -839,6 +838,7 @@ OBJEXT CPP GREP EGREP +ENDIAN LLVM_CROSS_COMPILING BUILD_CC BUILD_EXEEXT @@ -4183,9 +4183,19 @@ echo "${ECHO_T}$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in yes) ENDIAN=big +, + +cat >>confdefs.h <<\_ACEOF +#define MSB_FIRST 1 +_ACEOF ;; no) ENDIAN=little +, + +cat >>confdefs.h <<\_ACEOF +#define LSB_FIRST 1 +_ACEOF ;; *) { { echo "$as_me:$LINENO: error: unknown endianness @@ -8220,7 +8230,9 @@ if test "${enable_ltdl_install+set}" = set; then fi - if test x"${enable_ltdl_install-no}" != xno; then + + +if test x"${enable_ltdl_install-no}" != xno; then INSTALL_LTDL_TRUE= INSTALL_LTDL_FALSE='#' else @@ -8228,7 +8240,9 @@ else INSTALL_LTDL_FALSE= fi - if test x"${enable_ltdl_convenience-no}" != xno; then + + +if test x"${enable_ltdl_convenience-no}" != xno; then CONVENIENCE_LTDL_TRUE= CONVENIENCE_LTDL_FALSE='#' else @@ -9867,7 +9881,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext + echo '#line 11819 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -13442,11 +13456,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13445: $lt_compile\"" >&5) + (eval echo "\"\$as_me:13459: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:13449: \$? = $ac_status" >&5 + echo "$as_me:13463: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -13710,11 +13724,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13713: $lt_compile\"" >&5) + (eval echo "\"\$as_me:13727: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:13717: \$? = $ac_status" >&5 + echo "$as_me:13731: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -13814,11 +13828,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13817: $lt_compile\"" >&5) + (eval echo "\"\$as_me:13831: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:13821: \$? = $ac_status" >&5 + echo "$as_me:13835: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -16122,7 +16136,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:18575: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:18565: \$? = $ac_status" >&5 + echo "$as_me:18579: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -18662,11 +18676,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:18665: $lt_compile\"" >&5) + (eval echo "\"\$as_me:18679: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:18669: \$? = $ac_status" >&5 + echo "$as_me:18683: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -20232,11 +20246,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:20235: $lt_compile\"" >&5) + (eval echo "\"\$as_me:20249: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:20239: \$? = $ac_status" >&5 + echo "$as_me:20253: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -20336,11 +20350,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:20339: $lt_compile\"" >&5) + (eval echo "\"\$as_me:20353: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:20343: \$? = $ac_status" >&5 + echo "$as_me:20357: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -22539,11 +22553,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:22542: $lt_compile\"" >&5) + (eval echo "\"\$as_me:22556: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:22546: \$? = $ac_status" >&5 + echo "$as_me:22560: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -22807,11 +22821,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:22810: $lt_compile\"" >&5) + (eval echo "\"\$as_me:22824: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:22814: \$? = $ac_status" >&5 + echo "$as_me:22828: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -22911,11 +22925,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:22914: $lt_compile\"" >&5) + (eval echo "\"\$as_me:22928: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:22918: \$? = $ac_status" >&5 + echo "$as_me:22932: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -32755,7 +32769,6 @@ NOLINKALL!$NOLINKALL$ac_delim LLVM_ON_UNIX!$LLVM_ON_UNIX$ac_delim LLVM_ON_WIN32!$LLVM_ON_WIN32$ac_delim ARCH!$ARCH$ac_delim -ENDIAN!$ENDIAN$ac_delim CC!$CC$ac_delim CFLAGS!$CFLAGS$ac_delim LDFLAGS!$LDFLAGS$ac_delim @@ -32766,6 +32779,7 @@ OBJEXT!$OBJEXT$ac_delim CPP!$CPP$ac_delim GREP!$GREP$ac_delim EGREP!$EGREP$ac_delim +ENDIAN!$ENDIAN$ac_delim LLVM_CROSS_COMPILING!$LLVM_CROSS_COMPILING$ac_delim BUILD_CC!$BUILD_CC$ac_delim BUILD_EXEEXT!$BUILD_EXEEXT$ac_delim diff --git a/include/llvm/Config/config.h.in b/include/llvm/Config/config.h.in index 32e154a695f..64858f26b94 100644 --- a/include/llvm/Config/config.h.in +++ b/include/llvm/Config/config.h.in @@ -494,6 +494,9 @@ /* Installation prefix directory */ #undef LLVM_PREFIX +/* Define if this target is little endian */ +#undef LSB_FIRST + /* Define if the OS needs help to load dependent libraries for dlopen(). */ #undef LTDL_DLOPEN_DEPLIBS @@ -511,6 +514,9 @@ /* Define to the system default library search path. */ #undef LTDL_SYSSEARCHPATH +/* Define if this target is big endian */ +#undef MSB_FIRST + /* Define if /dev/zero should be used when mapping RWX memory, or undefine if its not necessary */ #undef NEED_DEV_ZERO_FOR_MMAP diff --git a/include/llvm/Target/TargetData.h b/include/llvm/Target/TargetData.h index 3136a38ac0f..fd52ef380ab 100644 --- a/include/llvm/Target/TargetData.h +++ b/include/llvm/Target/TargetData.h @@ -23,6 +23,7 @@ #include "llvm/Pass.h" #include "llvm/Support/DataTypes.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Config/config.h" #include namespace llvm { @@ -142,6 +143,16 @@ public: bool isLittleEndian() const { return LittleEndian; } bool isBigEndian() const { return !LittleEndian; } + /// Host endianness... + bool hostIsLittleEndian() const { +#ifdef LSB_FIRST + return true; +#else + return false; +#endif + } + bool hostIsBigEndian() const { return !hostIsLittleEndian(); } + /// getStringRepresentation - Return the string representation of the /// TargetData. This representation is in the same format accepted by the /// string constructor above. diff --git a/lib/ExecutionEngine/ExecutionEngine.cpp b/lib/ExecutionEngine/ExecutionEngine.cpp index 02b94cc1fdb..2c7a7dd1443 100644 --- a/lib/ExecutionEngine/ExecutionEngine.cpp +++ b/lib/ExecutionEngine/ExecutionEngine.cpp @@ -633,20 +633,27 @@ void ExecutionEngine::StoreValueToMemory(const GenericValue &Val, GenericValue * switch (Ty->getTypeID()) { case Type::IntegerTyID: { unsigned BitWidth = cast(Ty)->getBitWidth(); - GenericValue TmpVal = Val; - if (BitWidth <= 8) - *((uint8_t*)Ptr) = uint8_t(Val.IntVal.getZExtValue()); - else if (BitWidth <= 16) { - *((uint16_t*)Ptr) = uint16_t(Val.IntVal.getZExtValue()); - } else if (BitWidth <= 32) { - *((uint32_t*)Ptr) = uint32_t(Val.IntVal.getZExtValue()); - } else if (BitWidth <= 64) { - *((uint64_t*)Ptr) = uint64_t(Val.IntVal.getZExtValue()); - } else { - uint64_t *Dest = (uint64_t*)Ptr; - const uint64_t *Src = Val.IntVal.getRawData(); - for (uint32_t i = 0; i < Val.IntVal.getNumWords(); ++i) - Dest[i] = Src[i]; + unsigned StoreBytes = (BitWidth + 7)/8; + uint8_t *Src = (uint8_t *)Val.IntVal.getRawData(); + uint8_t *Dst = (uint8_t *)Ptr; + + if (getTargetData()->hostIsLittleEndian()) + // Little-endian host - the source is ordered from LSB to MSB. + // Order the destination from LSB to MSB: Do a straight copy. + memcpy(Dst, Src, StoreBytes); + else { + // Big-endian host - the source is an array of 64 bit words ordered from + // LSW to MSW. Each word is ordered from MSB to LSB. + // Order the destination from MSB to LSB: Reverse the word order, but not + // the bytes in a word. + while (StoreBytes > sizeof(uint64_t)) { + StoreBytes -= sizeof(uint64_t); + // May not be aligned so use memcpy. + memcpy(Dst + StoreBytes, Src, sizeof(uint64_t)); + Src += sizeof(uint64_t); + } + + memcpy(Dst, Src + sizeof(uint64_t) - StoreBytes, StoreBytes); } break; } @@ -683,16 +690,32 @@ void ExecutionEngine::LoadValueFromMemory(GenericValue &Result, switch (Ty->getTypeID()) { case Type::IntegerTyID: { unsigned BitWidth = cast(Ty)->getBitWidth(); - if (BitWidth <= 8) - Result.IntVal = APInt(BitWidth, *((uint8_t*)Ptr)); - else if (BitWidth <= 16) { - Result.IntVal = APInt(BitWidth, *((uint16_t*)Ptr)); - } else if (BitWidth <= 32) { - Result.IntVal = APInt(BitWidth, *((uint32_t*)Ptr)); - } else if (BitWidth <= 64) { - Result.IntVal = APInt(BitWidth, *((uint64_t*)Ptr)); - } else - Result.IntVal = APInt(BitWidth, (BitWidth+63)/64, (uint64_t*)Ptr); + unsigned LoadBytes = (BitWidth + 7)/8; + + // An APInt with all words initially zero. + Result.IntVal = APInt(BitWidth, 0); + + uint8_t *Src = (uint8_t *)Ptr; + uint8_t *Dst = (uint8_t *)Result.IntVal.getRawData(); + + if (getTargetData()->hostIsLittleEndian()) + // Little-endian host - the destination must be ordered from LSB to MSB. + // The source is ordered from LSB to MSB: Do a straight copy. + memcpy(Dst, Src, LoadBytes); + else { + // Big-endian - the destination is an array of 64 bit words ordered from + // LSW to MSW. Each word must be ordered from MSB to LSB. The source is + // ordered from MSB to LSB: Reverse the word order, but not the bytes in + // a word. + while (LoadBytes > sizeof(uint64_t)) { + LoadBytes -= sizeof(uint64_t); + // May not be aligned so use memcpy. + memcpy(Dst, Src + LoadBytes, sizeof(uint64_t)); + Dst += sizeof(uint64_t); + } + + memcpy(Dst + sizeof(uint64_t) - LoadBytes, Src, LoadBytes); + } break; } case Type::FloatTyID: diff --git a/test/ExecutionEngine/2007-12-10-APIntLoadStore.ll b/test/ExecutionEngine/2007-12-10-APIntLoadStore.ll new file mode 100644 index 00000000000..7a337f90cb6 --- /dev/null +++ b/test/ExecutionEngine/2007-12-10-APIntLoadStore.ll @@ -0,0 +1,19 @@ +; RUN: llvm-as < %s -o - | lli -force-interpreter +; PR1836 + +define i32 @main() { +entry: + %retval = alloca i32 ; [#uses=2] + %tmp = alloca i32 ; [#uses=2] + %x = alloca i75, align 16 ; [#uses=1] + %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] + store i75 999, i75* %x, align 16 + store i32 0, i32* %tmp, align 4 + %tmp1 = load i32* %tmp, align 4 ; [#uses=1] + store i32 %tmp1, i32* %retval, align 4 + br label %return + +return: ; preds = %entry + %retval2 = load i32* %retval ; [#uses=1] + ret i32 %retval2 +}