diff options
author | Dave Allison <dallison@google.com> | 2013-10-22 17:52:19 -0700 |
---|---|---|
committer | Dave Allison <dallison@google.com> | 2013-11-05 16:48:53 -0800 |
commit | 7020278bce98a0735dc6abcbd33bdf1ed2634f1d (patch) | |
tree | 533cf9a77046a49525d916043a58949e59319f2d /runtime | |
parent | 67f128a4023bbbe55827dd2c11ed0538ee387233 (diff) | |
download | art-7020278bce98a0735dc6abcbd33bdf1ed2634f1d.zip art-7020278bce98a0735dc6abcbd33bdf1ed2634f1d.tar.gz art-7020278bce98a0735dc6abcbd33bdf1ed2634f1d.tar.bz2 |
Support hardware divide instruction
Bug: 11299025
Uses sdiv for division and a combo of sdiv, mul and sub for modulus.
Only does this on processors that are capable of the sdiv instruction, as determined
by the build system.
Also provides a command line arg --instruction-set-features= to allow cross compilation.
Makefile adds the --instruction-set-features= arg to build-time dex2oat runs and defaults
it to something obtained from the target architecture.
Provides a GetInstructionSetFeatures() function on CompilerDriver that can be
queried for various features. The only feature supported right now is hasDivideInstruction().
Also adds a few more instructions to the ARM disassembler
b/11535253 is an addition to this CL to be done later.
Change-Id: Ia8aaf801fd94bc71e476902749cf20f74eba9f68
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/Android.mk | 1 | ||||
-rw-r--r-- | runtime/arch/arm/arm_sdiv.S | 24 | ||||
-rw-r--r-- | runtime/base/macros.h | 4 | ||||
-rw-r--r-- | runtime/common_test.h | 124 | ||||
-rw-r--r-- | runtime/instruction_set.h | 50 | ||||
-rw-r--r-- | runtime/oat.cc | 11 | ||||
-rw-r--r-- | runtime/oat.h | 3 | ||||
-rw-r--r-- | runtime/utils.cc | 29 | ||||
-rw-r--r-- | runtime/utils.h | 9 |
9 files changed, 250 insertions, 5 deletions
diff --git a/runtime/Android.mk b/runtime/Android.mk index 3d275e6..bef4381 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -187,6 +187,7 @@ LIBART_TARGET_SRC_FILES += \ arch/arm/jni_entrypoints_arm.S \ arch/arm/portable_entrypoints_arm.S \ arch/arm/quick_entrypoints_arm.S \ + arch/arm/arm_sdiv.S \ arch/arm/thread_arm.cc else # TARGET_ARCH != arm ifeq ($(TARGET_ARCH),x86) diff --git a/runtime/arch/arm/arm_sdiv.S b/runtime/arch/arm/arm_sdiv.S new file mode 100644 index 0000000..925e428 --- /dev/null +++ b/runtime/arch/arm/arm_sdiv.S @@ -0,0 +1,24 @@ +// This function is used to check for the CPU's support for the sdiv +// instruction at runtime. It will either return the value 1 or +// will cause an invalid instruction trap (SIGILL signal). The +// caller must arrange for the signal handler to set the r0 +// register to 0 and move the pc forward by 4 bytes (to skip +// the invalid instruction). + + +#include "asm_support_arm.S" + +.section .text +ENTRY CheckForARMSDIVInstruction + mov r1,#1 + // depending on the architecture, the assembler will not allow an + // sdiv instruction, so we will have to output the bytes directly. + + // sdiv r0,r1,r1 is two words: 0xfb91 0xf1f0. We need little endian. + .byte 0x91,0xfb,0xf1,0xf0 + + // if the divide worked, r0 will have the value #1 (result of sdiv). + // It will have 0 otherwise (set by the signal handler) + // the value is just returned from this function. + bx lr + END CheckForARMSDIVInstruction diff --git a/runtime/base/macros.h b/runtime/base/macros.h index d00c64a..00a530a 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -130,6 +130,10 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define LIKELY(x) __builtin_expect((x), true) #define UNLIKELY(x) __builtin_expect((x), false) +// Stringify the argument. +#define QUOTE(x) #x +#define STRINGIFY(x) QUOTE(x) + #ifndef NDEBUG #define ALWAYS_INLINE #else diff --git a/runtime/common_test.h b/runtime/common_test.h index 673a03b..79fa680 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -22,6 +22,7 @@ #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> +#include <fstream> #include "../../external/icu4c/common/unicode/uvernum.h" #include "base/macros.h" @@ -152,6 +153,113 @@ class ScratchFile { UniquePtr<File> file_; }; +#if defined(__arm__) + + +#include <signal.h> +#include <asm/sigcontext.h> +#include <asm/ucontext.h> + + +// A signal handler called when have an illegal instruction. We record the fact in +// a global boolean and then increment the PC in the signal context to return to +// the next instruction. We know the instruction is an sdiv (4 bytes long). +static void baddivideinst(int signo, siginfo *si, void *data) { + (void)signo; + (void)si; + struct ucontext *uc = (struct ucontext *)data; + struct sigcontext *sc = &uc->uc_mcontext; + sc->arm_r0 = 0; // set R0 to #0 to signal error + sc->arm_pc += 4; // skip offending instruction +} + +// This is in arch/arm/arm_sdiv.S. It does the following: +// mov r1,#1 +// sdiv r0,r1,r1 +// bx lr +// +// the result will be the value 1 if sdiv is supported. If it is not supported +// a SIGILL signal will be raised and the signal handler (baddivideinst) called. +// The signal handler sets r0 to #0 and then increments pc beyond the failed instruction. +// Thus if the instruction is not supported, the result of this function will be #0 + +extern "C" bool CheckForARMSDIVInstruction(); + +static InstructionSetFeatures GuessInstructionFeatures() { + InstructionSetFeatures f; + + // Uncomment this for processing of /proc/cpuinfo. + if (false) { + // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that + // the kernel puts the appropriate feature flags in here. Sometimes it doesn't. + std::ifstream in("/proc/cpuinfo"); + if (in) { + while (!in.eof()) { + std::string line; + std::getline(in, line); + if (!in.eof()) { + if (line.find("Features") != std::string::npos) { + if (line.find("idivt") != std::string::npos) { + f.SetHasDivideInstruction(true); + } + } + } + in.close(); + } + } else { + LOG(INFO) << "Failed to open /proc/cpuinfo"; + } + } + + // See if have a sdiv instruction. Register a signal handler and try to execute + // an sdiv instruction. If we get a SIGILL then it's not supported. We can't use + // the /proc/cpuinfo method for this because Krait devices don't always put the idivt + // feature in the list. + struct sigaction sa, osa; + sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; + sa.sa_sigaction = baddivideinst; + sigaction(SIGILL, &sa, &osa); + + if (CheckForARMSDIVInstruction()) { + f.SetHasDivideInstruction(true); + } + + // Restore the signal handler. + sigaction(SIGILL, &osa, NULL); + + // Other feature guesses in here. + return f; +} + +#endif + +// Given a set of instruction features from the build, parse it. The +// input 'str' is a comma separated list of feature names. Parse it and +// return the InstructionSetFeatures object. +static InstructionSetFeatures ParseFeatureList(std::string str) { + LOG(INFO) << "Parsing features " << str; + InstructionSetFeatures result; + typedef std::vector<std::string> FeatureList; + FeatureList features; + Split(str, ',', features); + for (FeatureList::iterator i = features.begin(); i != features.end(); i++) { + std::string feature = Trim(*i); + if (feature == "default") { + // Nothing to do. + } else if (feature == "div") { + // Supports divide instruction. + result.SetHasDivideInstruction(true); + } else if (feature == "nodiv") { + // Turn off support for divide instruction. + result.SetHasDivideInstruction(false); + } else { + LOG(FATAL) << "Unknown instruction set feature: '" << feature << "'"; + } + } + // Others... + return result; +} + class CommonTest : public testing::Test { public: static void MakeExecutable(const mirror::ByteArray* code_array) { @@ -314,8 +422,22 @@ class CommonTest : public testing::Test { class_linker_ = runtime_->GetClassLinker(); InstructionSet instruction_set = kNone; + + // take the default set of instruction features from the build if present + InstructionSetFeatures instruction_set_features = +#ifdef ART_DEFAULT_INSTRUCTION_SET_FEATURES + ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES)); +#else + ParseFeatureList("default"); +#endif + #if defined(__arm__) instruction_set = kThumb2; + InstructionSetFeatures runtime_features = GuessInstructionFeatures(); + + // for ARM, do a runtime check to make sure that the features we are passed from + // the build match the features we actually determine at runtime. + ASSERT_EQ(instruction_set_features, runtime_features); #elif defined(__mips__) instruction_set = kMips; #elif defined(__i386__) @@ -338,6 +460,7 @@ class CommonTest : public testing::Test { } class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set, + instruction_set_features, true, new CompilerDriver::DescriptorSet, 2, true)); } @@ -568,7 +691,6 @@ class CheckJniAbortCatcher { #else #define TEST_DISABLED_FOR_PORTABLE() #endif - } // namespace art namespace std { diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index 2217f7f..aee7447 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -18,6 +18,9 @@ #define ART_RUNTIME_INSTRUCTION_SET_H_ #include <iosfwd> +#include <string> + +#include "base/macros.h" namespace art { @@ -29,6 +32,53 @@ enum InstructionSet { kMips }; +enum InstructionFeatures { + kHwDiv = 1 // Supports hardware divide. +}; + +// This is a bitmask of supported features per architecture. +class PACKED(4) InstructionSetFeatures { + public: + InstructionSetFeatures() : mask_(0) {} + explicit InstructionSetFeatures(uint32_t mask) : mask_(mask) {} + + bool HasDivideInstruction() const { + return (mask_ & kHwDiv) != 0; + } + + void SetHasDivideInstruction(bool v) { + mask_ = (mask_ & ~kHwDiv) | (v ? kHwDiv : 0); + } + + std::string GetFeatureString() const { + std::string result; + if ((mask_ & kHwDiv) != 0) { + result += "div"; + } + if (result.size() == 0) { + result = "none"; + } + return result; + } + + uint32_t get_mask() const { + return mask_; + } + + // Other features in here. + + bool operator==(const InstructionSetFeatures &peer) const { + return mask_ == peer.mask_; + } + + bool operator!=(const InstructionSetFeatures &peer) const { + return mask_ != peer.mask_; + } + + private: + uint32_t mask_; +}; + std::ostream& operator<<(std::ostream& os, const InstructionSet& rhs); } // namespace art diff --git a/runtime/oat.cc b/runtime/oat.cc index defda6b..9489795 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,13 +22,14 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '0', '9', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '1', '0', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); } OatHeader::OatHeader(InstructionSet instruction_set, + const InstructionSetFeatures& instruction_set_features, const std::vector<const DexFile*>* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, @@ -42,6 +43,9 @@ OatHeader::OatHeader(InstructionSet instruction_set, instruction_set_ = instruction_set; UpdateChecksum(&instruction_set_, sizeof(instruction_set_)); + instruction_set_features_ = instruction_set_features; + UpdateChecksum(&instruction_set_features_, sizeof(instruction_set_features_)); + dex_file_count_ = dex_files->size(); UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_)); @@ -99,6 +103,11 @@ InstructionSet OatHeader::GetInstructionSet() const { return instruction_set_; } +const InstructionSetFeatures& OatHeader::GetInstructionSetFeatures() const { + CHECK(IsValid()); + return instruction_set_features_; +} + uint32_t OatHeader::GetExecutableOffset() const { DCHECK(IsValid()); DCHECK_ALIGNED(executable_offset_, kPageSize); diff --git a/runtime/oat.h b/runtime/oat.h index c864c2c..de840b5 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,6 +32,7 @@ class PACKED(4) OatHeader { OatHeader(); OatHeader(InstructionSet instruction_set, + const InstructionSetFeatures& instruction_set_features, const std::vector<const DexFile*>* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, @@ -80,6 +81,7 @@ class PACKED(4) OatHeader { void SetQuickToInterpreterBridgeOffset(uint32_t offset); InstructionSet GetInstructionSet() const; + const InstructionSetFeatures& GetInstructionSetFeatures() const; uint32_t GetImageFileLocationOatChecksum() const; uint32_t GetImageFileLocationOatDataBegin() const; uint32_t GetImageFileLocationSize() const; @@ -92,6 +94,7 @@ class PACKED(4) OatHeader { uint32_t adler32_checksum_; InstructionSet instruction_set_; + InstructionSetFeatures instruction_set_features_; uint32_t dex_file_count_; uint32_t executable_offset_; uint32_t interpreter_to_interpreter_bridge_offset_; diff --git a/runtime/utils.cc b/runtime/utils.cc index 9796b99..e039581 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -889,6 +889,35 @@ void Split(const std::string& s, char separator, std::vector<std::string>& resul } } +std::string Trim(std::string s) { + std::string result; + unsigned int start_index = 0; + unsigned int end_index = s.size() - 1; + + // Skip initial whitespace. + while (start_index < s.size()) { + if (!isspace(s[start_index])) { + break; + } + start_index++; + } + + // Skip terminating whitespace. + while (end_index >= start_index) { + if (!isspace(s[end_index])) { + break; + } + end_index--; + } + + // All spaces, no beef. + if (end_index < start_index) { + return ""; + } + // Start_index is the first non-space, end_index is the last one. + return s.substr(start_index, end_index - start_index + 1); +} + template <typename StringT> std::string Join(std::vector<StringT>& strings, char separator) { if (strings.empty()) { diff --git a/runtime/utils.h b/runtime/utils.h index 51035b6..6850e8b 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -252,7 +252,7 @@ std::string FormatDuration(uint64_t nano_duration, TimeUnit time_unit); // Get the appropriate unit for a nanosecond duration. TimeUnit GetAppropriateTimeUnit(uint64_t nano_duration); -// Get the divisor to convert from a nanoseconds to a time unit +// Get the divisor to convert from a nanoseconds to a time unit. uint64_t GetNsToTimeUnitDivisor(TimeUnit time_unit); // Performs JNI name mangling as described in section 11.3 "Linking Native Methods" @@ -326,6 +326,9 @@ void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts // strings. Empty strings will be omitted. void Split(const std::string& s, char separator, std::vector<std::string>& result); +// Trims whitespace off both ends of the given string. +std::string Trim(std::string s); + // Joins a vector of strings into a single string, using the given separator. template <typename StringT> std::string Join(std::vector<StringT>& strings, char separator); @@ -354,10 +357,10 @@ void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix = "", bool // Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86. void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true); -// Find $ANDROID_ROOT, /system, or abort +// Find $ANDROID_ROOT, /system, or abort. const char* GetAndroidRoot(); -// Find $ANDROID_DATA, /data, or abort +// Find $ANDROID_DATA, /data, or abort. const char* GetAndroidData(); // Returns the dalvik-cache location, or dies trying. |