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
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 @@
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 @@
#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 @@
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_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_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 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+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 @@
// 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 @@
// 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 @@
// 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.