diff options
Diffstat (limited to 'runtime/instruction_set.cc')
-rw-r--r-- | runtime/instruction_set.cc | 388 |
1 files changed, 382 insertions, 6 deletions
diff --git a/runtime/instruction_set.cc b/runtime/instruction_set.cc index 644e055..b5f8571 100644 --- a/runtime/instruction_set.cc +++ b/runtime/instruction_set.cc @@ -16,6 +16,12 @@ #include "instruction_set.h" +#include <fstream> + +#include "base/casts.h" +#include "base/stringprintf.h" +#include "utils.h" + namespace art { const char* GetInstructionSetString(const InstructionSet isa) { @@ -35,7 +41,7 @@ const char* GetInstructionSetString(const InstructionSet isa) { return "none"; default: LOG(FATAL) << "Unknown ISA " << isa; - return nullptr; + UNREACHABLE(); } } @@ -117,15 +123,385 @@ size_t GetStackOverflowReservedBytes(InstructionSet isa) { } } -std::string InstructionSetFeatures::GetFeatureString() const { +const InstructionSetFeatures* InstructionSetFeatures::FromVariant(InstructionSet isa, + const std::string& variant, + std::string* error_msg) { + const InstructionSetFeatures* result; + switch (isa) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromVariant(variant, error_msg); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(isa); + break; + } + CHECK_EQ(result == nullptr, error_msg->size() != 0); + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromFeatureString(InstructionSet isa, + const std::string& feature_list, + std::string* error_msg) { + const InstructionSetFeatures* result; + switch (isa) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromFeatureString(feature_list, error_msg); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(isa); + break; + } + // TODO: warn if feature_list doesn't agree with result's GetFeatureList(). + CHECK_EQ(result == nullptr, error_msg->size() != 0); + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromBitmap(InstructionSet isa, + uint32_t bitmap) { + const InstructionSetFeatures* result; + switch (isa) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromBitmap(bitmap); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(isa); + break; + } + CHECK_EQ(bitmap, result->AsBitmap()); + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromCppDefines() { + const InstructionSetFeatures* result; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromCppDefines(); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA); + break; + } + return result; +} + + +const InstructionSetFeatures* InstructionSetFeatures::FromCpuInfo() { + const InstructionSetFeatures* result; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromCpuInfo(); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA); + break; + } + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromHwcap() { + const InstructionSetFeatures* result; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromHwcap(); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA); + break; + } + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromAssembly() { + const InstructionSetFeatures* result; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromAssembly(); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA); + break; + } + return result; +} + +const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const { + DCHECK_EQ(kArm, GetInstructionSet()); + return down_cast<const ArmInstructionSetFeatures*>(this); +} + +std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) { + os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString(); + return os; +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromFeatureString( + const std::string& feature_list, std::string* error_msg) { + std::vector<std::string> features; + Split(feature_list, ',', &features); + bool has_lpae = false; + bool has_div = false; + for (auto i = features.begin(); i != features.end(); i++) { + std::string feature = Trim(*i); + if (feature == "default" || feature == "none") { + // Nothing to do. + } else if (feature == "div") { + has_div = true; + } else if (feature == "nodiv") { + has_div = false; + } else if (feature == "lpae") { + has_lpae = true; + } else if (feature == "nolpae") { + has_lpae = false; + } else { + *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); + return nullptr; + } + } + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromVariant( + const std::string& variant, std::string* error_msg) { + // Look for variants that have divide support. + bool has_div = false; + { + static const char* arm_variants_with_div[] = { + "cortex-a7", "cortex-a12", "cortex-a15", "cortex-a17", "cortex-a53", "cortex-a57", + "cortex-m3", "cortex-m4", "cortex-r4", "cortex-r5", + "cyclone", "denver", "krait", "swift" + }; + for (const char* div_variant : arm_variants_with_div) { + if (variant == div_variant) { + has_div = true; + break; + } + } + } + // Look for variants that have LPAE support. + bool has_lpae = false; + { + static const char* arm_variants_with_lpae[] = { + "cortex-a7", "cortex-a15", "krait", "denver" + }; + for (const char* lpae_variant : arm_variants_with_lpae) { + if (variant == lpae_variant) { + has_lpae = true; + break; + } + } + } + if (has_div == false && has_lpae == false) { + // Avoid unsupported variants. + static const char* unsupported_arm_variants[] = { + // ARM processors that aren't ARMv7 compatible aren't supported. + "arm2", "arm250", "arm3", "arm6", "arm60", "arm600", "arm610", "arm620", + "cortex-m0", "cortex-m0plus", "cortex-m1", + "fa526", "fa626", "fa606te", "fa626te", "fmp626", "fa726te", + "iwmmxt", "iwmmxt2", + "strongarm", "strongarm110", "strongarm1100", "strongarm1110", + "xscale" + }; + for (const char* us_variant : unsupported_arm_variants) { + if (variant == us_variant) { + *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", us_variant); + return nullptr; + } + } + // Warn if the variant is unknown. + // TODO: some of the variants below may have feature support, but that support is currently + // unknown so we'll choose conservative (sub-optimal) defaults without warning. + // TODO: some of the architectures may not support all features required by ART and should be + // moved to unsupported_arm_variants[] above. + static const char* arm_variants_without_known_features[] = { + "arm7", "arm7m", "arm7d", "arm7dm", "arm7di", "arm7dmi", "arm70", "arm700", "arm700i", + "arm710", "arm710c", "arm7100", "arm720", "arm7500", "arm7500fe", "arm7tdmi", "arm7tdmi-s", + "arm710t", "arm720t", "arm740t", + "arm8", "arm810", + "arm9", "arm9e", "arm920", "arm920t", "arm922t", "arm946e-s", "arm966e-s", "arm968e-s", + "arm926ej-s", "arm940t", "arm9tdmi", + "arm10tdmi", "arm1020t", "arm1026ej-s", "arm10e", "arm1020e", "arm1022e", + "arm1136j-s", "arm1136jf-s", + "arm1156t2-s", "arm1156t2f-s", "arm1176jz-s", "arm1176jzf-s", + "cortex-a5", "cortex-a8", "cortex-a9", "cortex-a9-mp", "cortex-r4f", + "marvell-pj4", "mpcore", "mpcorenovfp" + }; + bool found = false; + for (const char* ff_variant : arm_variants_without_known_features) { + if (variant == ff_variant) { + found = true; + break; + } + } + if (!found) { + LOG(WARNING) << "Unknown instruction set features for ARM CPU variant (" << variant + << ") using conservative defaults"; + } + } + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) { + bool has_lpae = (bitmap & kLpaeBitfield) != 0; + bool has_div = (bitmap & kDivBitfield) != 0; + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCppDefines() { +#if defined(__ARM_ARCH_EXT_IDIV__) + bool has_div = true; +#else + bool has_div = false; +#endif +#if defined(__ARM_FEATURE_LPAE) + bool has_lpae = true; +#else + bool has_lpae = false; +#endif + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCpuInfo() { + // 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. + bool has_lpae = false; + bool has_div = false; + + std::ifstream in("/proc/cpuinfo"); + if (!in.fail()) { + while (!in.eof()) { + std::string line; + std::getline(in, line); + if (!in.eof()) { + LOG(INFO) << "cpuinfo line: " << line; + if (line.find("Features") != std::string::npos) { + LOG(INFO) << "found features"; + if (line.find("idivt") != std::string::npos) { + // We always expect both ARM and Thumb divide instructions to be available or not + // available. + CHECK_NE(line.find("idiva"), std::string::npos); + has_div = true; + } + if (line.find("lpae") != std::string::npos) { + has_lpae = true; + } + } + } + } + in.close(); + } else { + LOG(INFO) << "Failed to open /proc/cpuinfo"; + } + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +#if defined(HAVE_ANDROID_OS) && defined(__arm__) +#include <sys/auxv.h> +#include <asm/hwcap.h> +#endif + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromHwcap() { + bool has_lpae = false; + bool has_div = false; + +#if defined(HAVE_ANDROID_OS) && defined(__arm__) + uint64_t hwcaps = getauxval(AT_HWCAP); + LOG(INFO) << "hwcaps=" << hwcaps; + if ((hwcaps & HWCAP_IDIVT) != 0) { + // We always expect both ARM and Thumb divide instructions to be available or not + // available. + CHECK_NE(hwcaps & HWCAP_IDIVA, 0U); + has_div = true; + } + if ((hwcaps & HWCAP_LPAE) != 0) { + has_lpae = true; + } +#endif + + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +// A signal handler called by a fault for an illegal instruction. We record the fact in r0 +// 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 bad_divide_inst_handle(int signo, siginfo *si, void *data) { + UNUSED(signo); + UNUSED(si); +#if defined(__arm__) + 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. +#else + UNUSED(data); +#endif +} + +#if defined(__arm__) +extern "C" bool artCheckForARMSDIVInstruction(); +#endif + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromAssembly() { + // 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. + struct sigaction sa, osa; + sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; + sa.sa_sigaction = bad_divide_inst_handle; + sigaction(SIGILL, &sa, &osa); + + bool has_div = false; +#if defined(__arm__) + if (artCheckForARMSDIVInstruction()) { + has_div = true; + } +#endif + + // Restore the signal handler. + sigaction(SIGILL, &osa, nullptr); + + // Use compile time features to "detect" LPAE support. + // TODO: write an assembly LPAE support test. +#if defined(__ARM_FEATURE_LPAE) + bool has_lpae = true; +#else + bool has_lpae = false; +#endif + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + + +bool ArmInstructionSetFeatures::Equals(const InstructionSetFeatures* other) const { + if (kArm != other->GetInstructionSet()) { + return false; + } + const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures(); + return has_lpae_ == other_as_arm->has_lpae_ && has_div_ == other_as_arm->has_div_; +} + +uint32_t ArmInstructionSetFeatures::AsBitmap() const { + return (has_lpae_ ? kLpaeBitfield : 0) | (has_div_ ? kDivBitfield : 0); +} + +std::string ArmInstructionSetFeatures::GetFeatureString() const { std::string result; - if ((mask_ & kHwDiv) != 0) { - result += "div"; + if (has_div_) { + result += ",div"; + } + if (has_lpae_) { + result += ",lpae"; } if (result.size() == 0) { - result = "none"; + return "none"; + } else { + // Strip leading comma. + return result.substr(1, result.size()); } - return result; } } // namespace art |