diff options
166 files changed, 3713 insertions, 822 deletions
diff --git a/Android.mk b/Android.mk index 558986e562..e4f4e74cb2 100644 --- a/Android.mk +++ b/Android.mk @@ -67,6 +67,7 @@ include $(art_path)/tools/Android.mk  include $(art_path)/tools/ahat/Android.mk  include $(art_path)/tools/amm/Android.mk  include $(art_path)/tools/dexfuzz/Android.mk +include $(art_path)/tools/veridex/Android.mk  include $(art_path)/libart_fake/Android.mk  ART_HOST_DEPENDENCIES := \ diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 8681642c0e..b342abe17c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -37,6 +37,7 @@ GTEST_DEX_DIRECTORIES := \    ExceptionHandle \    GetMethodSignature \    HiddenApi \ +  HiddenApiSignatures \    ImageLayoutA \    ImageLayoutB \    IMTA \ @@ -160,6 +161,7 @@ ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEP  ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps  ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle  ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi +ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures  ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods  ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB  ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation diff --git a/compiler/Android.bp b/compiler/Android.bp index e42261c556..6bed48e107 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -70,6 +70,7 @@ art_cc_defaults {          "optimizing/load_store_analysis.cc",          "optimizing/load_store_elimination.cc",          "optimizing/locations.cc", +        "optimizing/loop_analysis.cc",          "optimizing/loop_optimization.cc",          "optimizing/nodes.cc",          "optimizing/optimization.cc", diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 127833233a..4093833e0b 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -781,7 +781,8 @@ void CompilerDriver::Resolve(jobject class_loader,  // TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a  //       stable order. -static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache, +static void ResolveConstStrings(ClassLinker* class_linker, +                                Handle<mirror::DexCache> dex_cache,                                  const DexFile& dex_file,                                  const DexFile::CodeItem* code_item)        REQUIRES_SHARED(Locks::mutator_lock_) { @@ -790,7 +791,6 @@ static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache,      return;    } -  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();    for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) {      switch (inst->Opcode()) {        case Instruction::CONST_STRING: @@ -838,22 +838,105 @@ static void ResolveConstStrings(CompilerDriver* driver,            dex_file->StringByTypeIdx(class_def.class_idx_));        if (!compilation_enabled) {          // Compilation is skipped, do not resolve const-string in code of this class. -        // TODO: Make sure that inlining honors this. +        // FIXME: Make sure that inlining honors this. b/26687569          continue;        }        // Direct and virtual methods. -      int64_t previous_method_idx = -1;        while (it.HasNextMethod()) { -        uint32_t method_idx = it.GetMemberIndex(); -        if (method_idx == previous_method_idx) { -          // smali can create dex files with two encoded_methods sharing the same method_idx -          // http://code.google.com/p/smali/issues/detail?id=119 -          it.Next(); -          continue; +        ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); +        it.Next(); +      } +      DCHECK(!it.HasNext()); +    } +  } +} + +// Initialize type check bit strings for check-cast and instance-of in the code. Done to have +// deterministic allocation behavior. Right now this is single-threaded for simplicity. +// TODO: Collect the relevant type indices in parallel, then process them sequentially in a +//       stable order. + +static void InitializeTypeCheckBitstrings(CompilerDriver* driver, +                                          ClassLinker* class_linker, +                                          Handle<mirror::DexCache> dex_cache, +                                          const DexFile& dex_file, +                                          const DexFile::CodeItem* code_item) +      REQUIRES_SHARED(Locks::mutator_lock_) { +  if (code_item == nullptr) { +    // Abstract or native method. +    return; +  } + +  for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { +    switch (inst->Opcode()) { +      case Instruction::CHECK_CAST: +      case Instruction::INSTANCE_OF: { +        dex::TypeIndex type_index( +            (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c()); +        const char* descriptor = dex_file.StringByTypeIdx(type_index); +        // We currently do not use the bitstring type check for array or final (including +        // primitive) classes. We may reconsider this in future if it's deemed to be beneficial. +        // And we cannot use it for classes outside the boot image as we do not know the runtime +        // value of their bitstring when compiling (it may not even get assigned at runtime). +        if (descriptor[0] == 'L' && driver->IsImageClass(descriptor)) { +          ObjPtr<mirror::Class> klass = +              class_linker->LookupResolvedType(type_index, +                                               dex_cache.Get(), +                                               /* class_loader */ nullptr); +          CHECK(klass != nullptr) << descriptor << " should have been previously resolved."; +          // Now assign the bitstring if the class is not final. Keep this in sync with sharpening. +          if (!klass->IsFinal()) { +            MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); +            SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass); +          }          } -        previous_method_idx = method_idx; -        ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); +        break; +      } + +      default: +        break; +    } +  } +} + +static void InitializeTypeCheckBitstrings(CompilerDriver* driver, +                                          const std::vector<const DexFile*>& dex_files, +                                          TimingLogger* timings) { +  ScopedObjectAccess soa(Thread::Current()); +  StackHandleScope<1> hs(soa.Self()); +  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); +  MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr)); + +  for (const DexFile* dex_file : dex_files) { +    dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); +    TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); + +    size_t class_def_count = dex_file->NumClassDefs(); +    for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { +      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + +      const uint8_t* class_data = dex_file->GetClassData(class_def); +      if (class_data == nullptr) { +        // empty class, probably a marker interface +        continue; +      } + +      ClassDataItemIterator it(*dex_file, class_data); +      it.SkipAllFields(); + +      bool compilation_enabled = driver->IsClassToCompile( +          dex_file->StringByTypeIdx(class_def.class_idx_)); +      if (!compilation_enabled) { +        // Compilation is skipped, do not look for type checks in code of this class. +        // FIXME: Make sure that inlining honors this. b/26687569 +        continue; +      } + +      // Direct and virtual methods. +      while (it.HasNextMethod()) { +        InitializeTypeCheckBitstrings( +            driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem());          it.Next();        }        DCHECK(!it.HasNext()); @@ -955,6 +1038,14 @@ void CompilerDriver::PreCompile(jobject class_loader,    UpdateImageClasses(timings);    VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); + +  if (kBitstringSubtypeCheckEnabled && +      GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) { +    // Initialize type check bit string used by check-cast and instanceof. +    // Do this now to have a deterministic image. +    // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final. +    InitializeTypeCheckBitstrings(this, dex_files, timings); +  }  }  bool CompilerDriver::IsImageClass(const char* descriptor) const { diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index a4873202b2..3bd5e14539 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -438,6 +438,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {        case TypeCheckKind::kArrayCheck:        case TypeCheckKind::kUnresolvedCheck:          return false; +      case TypeCheckKind::kBitstringCheck: +        return true;      }      LOG(FATAL) << "Unreachable";      UNREACHABLE(); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a024df8537..273346ab4a 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2129,6 +2129,26 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod    __ Bind(slow_path->GetExitLabel());  } +void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare( +    HTypeCheckInstruction* check, vixl::aarch64::Register temp) { +  uint32_t path_to_root = check->GetBitstringPathToRoot(); +  uint32_t mask = check->GetBitstringMask(); +  DCHECK(IsPowerOfTwo(mask + 1)); +  size_t mask_bits = WhichPowerOf2(mask + 1); + +  if (mask_bits == 16u) { +    // Load only the bitstring part of the status word. +    __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset())); +  } else { +    // /* uint32_t */ temp = temp->status_ +    __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset())); +    // Extract the bitstring bits. +    __ Ubfx(temp, temp, 0, mask_bits); +  } +  // Compare the bitstring bits to `path_to_root`. +  __ Cmp(temp, path_to_root); +} +  void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {    BarrierType type = BarrierAll; @@ -3866,6 +3886,8 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {      case TypeCheckKind::kInterfaceCheck:        call_kind = LocationSummary::kCallOnSlowPath;        break; +    case TypeCheckKind::kBitstringCheck: +      break;    }    LocationSummary* locations = @@ -3874,7 +3896,13 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {      locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.    }    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    // The "out" register is used as a temporary, so it overlaps with the inputs.    // Note that TypeCheckSlowPathARM64 uses this register too.    locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -3887,7 +3915,9 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    Register obj = InputRegisterAt(instruction, 0); -  Register cls = InputRegisterAt(instruction, 1); +  Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) +      ? Register() +      : InputRegisterAt(instruction, 1);    Location out_loc = locations->Out();    Register out = OutputRegister(instruction);    const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -4073,6 +4103,23 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {        }        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        out_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, out); +      __ Cset(out, eq); +      if (zero.IsLinked()) { +        __ B(&done); +      } +      break; +    }    }    if (zero.IsLinked()) { @@ -4095,7 +4142,13 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations =        new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64.    locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));  } @@ -4105,7 +4158,9 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    Register obj = InputRegisterAt(instruction, 0); -  Register cls = InputRegisterAt(instruction, 1); +  Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) +      ? Register() +      : InputRegisterAt(instruction, 1);    const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);    DCHECK_GE(num_temps, 1u);    DCHECK_LE(num_temps, 3u); @@ -4286,6 +4341,20 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {        __ B(ne, &start_loop);        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        temp_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp2_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, temp); +      __ B(ne, type_check_slow_path->GetEntryLabel()); +      break; +    }    }    __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index a8a9802f9a..6a52eecbd3 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -264,6 +264,8 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator {   private:    void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,                                          vixl::aarch64::Register class_reg); +  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, +                                         vixl::aarch64::Register temp);    void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);    void HandleBinaryOp(HBinaryOperation* instr); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 6ebcc67b49..b38a006305 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7523,6 +7523,67 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(    __ Bind(slow_path->GetExitLabel());  } +void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare( +    HTypeCheckInstruction* check, +    vixl32::Register temp, +    vixl32::FlagsUpdate flags_update) { +  uint32_t path_to_root = check->GetBitstringPathToRoot(); +  uint32_t mask = check->GetBitstringMask(); +  DCHECK(IsPowerOfTwo(mask + 1)); +  size_t mask_bits = WhichPowerOf2(mask + 1); + +  // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs +  // the Z flag for BNE. This is indicated by the `flags_update` parameter. +  if (mask_bits == 16u) { +    // Load only the bitstring part of the status word. +    __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); +    // Check if the bitstring bits are equal to `path_to_root`. +    if (flags_update == SetFlags) { +      __ Cmp(temp, path_to_root); +    } else { +      __ Sub(temp, temp, path_to_root); +    } +  } else { +    // /* uint32_t */ temp = temp->status_ +    __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); +    if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) { +      // Compare the bitstring bits using SUB. +      __ Sub(temp, temp, path_to_root); +      // Shift out bits that do not contribute to the comparison. +      __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits)); +    } else if (IsUint<16>(path_to_root)) { +      if (temp.IsLow()) { +        // Note: Optimized for size but contains one more dependent instruction than necessary. +        //       MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the +        //       macro assembler would use the high reg IP for the constant by default. +        // Compare the bitstring bits using SUB. +        __ Sub(temp, temp, path_to_root & 0x00ffu);  // 16-bit SUB (immediate) T2 +        __ Sub(temp, temp, path_to_root & 0xff00u);  // 32-bit SUB (immediate) T3 +        // Shift out bits that do not contribute to the comparison. +        __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits)); +      } else { +        // Extract the bitstring bits. +        __ Ubfx(temp, temp, 0, mask_bits); +        // Check if the bitstring bits are equal to `path_to_root`. +        if (flags_update == SetFlags) { +          __ Cmp(temp, path_to_root); +        } else { +          __ Sub(temp, temp, path_to_root); +        } +      } +    } else { +      // Shift out bits that do not contribute to the comparison. +      __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits)); +      // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`. +      if (flags_update == SetFlags) { +        __ Cmp(temp, path_to_root << (32u - mask_bits)); +      } else { +        __ Sub(temp, temp, path_to_root << (32u - mask_bits)); +      } +    } +  } +} +  HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(      HLoadString::LoadKind desired_string_load_kind) {    switch (desired_string_load_kind) { @@ -7714,6 +7775,8 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {      case TypeCheckKind::kInterfaceCheck:        call_kind = LocationSummary::kCallOnSlowPath;        break; +    case TypeCheckKind::kBitstringCheck: +      break;    }    LocationSummary* locations = @@ -7722,7 +7785,13 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {      locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.    }    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    // The "out" register is used as a temporary, so it overlaps with the inputs.    // Note that TypeCheckSlowPathARM uses this register too.    locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7737,7 +7806,9 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction)    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    vixl32::Register obj = InputRegisterAt(instruction, 0); -  vixl32::Register cls = InputRegisterAt(instruction, 1); +  vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) +      ? vixl32::Register() +      : InputRegisterAt(instruction, 1);    Location out_loc = locations->Out();    vixl32::Register out = OutputRegister(instruction);    const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7977,6 +8048,26 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction)        __ B(slow_path->GetEntryLabel());        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        out_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, out, DontCare); +      // If `out` is a low reg and we would have another low reg temp, we could +      // optimize this as RSBS+ADC, see GenerateConditionWithZero(). +      // +      // Also, in some cases when `out` is a low reg and we're loading a constant to IP +      // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size +      // would be the same and we would have fewer direct data dependencies. +      codegen_->GenerateConditionWithZero(kCondEQ, out, out);  // CLZ+LSR +      break; +    }    }    if (done.IsReferenced()) { @@ -7994,7 +8085,13 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations =        new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));  } @@ -8003,7 +8100,9 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    vixl32::Register obj = InputRegisterAt(instruction, 0); -  vixl32::Register cls = InputRegisterAt(instruction, 1); +  vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) +      ? vixl32::Register() +      : InputRegisterAt(instruction, 1);    Location temp_loc = locations->GetTemp(0);    vixl32::Register temp = RegisterFrom(temp_loc);    const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -8188,6 +8287,20 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {        __ B(ne, &start_loop, /* far_target */ false);        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        temp_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp2_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags); +      __ B(ne, type_check_slow_path->GetEntryLabel()); +      break; +    }    }    if (done.IsReferenced()) {      __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 6a07e36022..2114ea1ba1 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -322,6 +322,9 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator {    void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);    void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path,                                          vixl32::Register class_reg); +  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, +                                         vixl::aarch32::Register temp, +                                         vixl::aarch32::FlagsUpdate flags_update);    void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);    void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);    void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index be9ff48a6b..25e2eddbfa 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1950,6 +1950,34 @@ void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCode    __ Bind(slow_path->GetExitLabel());  } +void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, +                                                                     Register temp) { +  uint32_t path_to_root = check->GetBitstringPathToRoot(); +  uint32_t mask = check->GetBitstringMask(); +  DCHECK(IsPowerOfTwo(mask + 1)); +  size_t mask_bits = WhichPowerOf2(mask + 1); + +  if (mask_bits == 16u) { +    // Load only the bitstring part of the status word. +    __ LoadFromOffset( +        kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); +    // Compare the bitstring bits using XOR. +    __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); +  } else { +    // /* uint32_t */ temp = temp->status_ +    __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); +    // Compare the bitstring bits using XOR. +    if (IsUint<16>(path_to_root)) { +      __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); +    } else { +      __ LoadConst32(TMP, path_to_root); +      __ Xor(temp, temp, TMP); +    } +    // Shift out bits that do not contribute to the comparison. +    __ Sll(temp, temp, 32 - mask_bits); +  } +} +  void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {    __ Sync(0);  // Only stype 0 is supported.  } @@ -3301,7 +3329,13 @@ void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations =        new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));  } @@ -3310,7 +3344,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    Register obj = obj_loc.AsRegister<Register>(); -  Register cls = locations->InAt(1).AsRegister<Register>(); +  Location cls = locations->InAt(1);    Location temp_loc = locations->GetTemp(0);    Register temp = temp_loc.AsRegister<Register>();    const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -3349,7 +3383,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {                                          kWithoutReadBarrier);        // Jump to slow path for throwing the exception or doing a        // more involved array check. -      __ Bne(temp, cls, slow_path->GetEntryLabel()); +      __ Bne(temp, cls.AsRegister<Register>(), slow_path->GetEntryLabel());        break;      } @@ -3375,7 +3409,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {        // exception.        __ Beqz(temp, slow_path->GetEntryLabel());        // Otherwise, compare the classes. -      __ Bne(temp, cls, &loop); +      __ Bne(temp, cls.AsRegister<Register>(), &loop);        break;      } @@ -3390,7 +3424,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {        // Walk over the class hierarchy to find a match.        MipsLabel loop;        __ Bind(&loop); -      __ Beq(temp, cls, &done); +      __ Beq(temp, cls.AsRegister<Register>(), &done);        // /* HeapReference<Class> */ temp = temp->super_class_        GenerateReferenceLoadOneRegister(instruction,                                         temp_loc, @@ -3413,7 +3447,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {                                          maybe_temp2_loc,                                          kWithoutReadBarrier);        // Do an exact check. -      __ Beq(temp, cls, &done); +      __ Beq(temp, cls.AsRegister<Register>(), &done);        // Otherwise, we need to check that the object's class is a non-primitive array.        // /* HeapReference<Class> */ temp = temp->component_type_        GenerateReferenceLoadOneRegister(instruction, @@ -3472,7 +3506,21 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {        // Go to next interface.        __ Addiu(TMP, TMP, -2);        // Compare the classes and continue the loop if they do not match. -      __ Bne(AT, cls, &loop); +      __ Bne(AT, cls.AsRegister<Register>(), &loop); +      break; +    } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        temp_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp2_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, temp); +      __ Bnez(temp, slow_path->GetEntryLabel());        break;      }    } @@ -7415,6 +7463,8 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {      case TypeCheckKind::kInterfaceCheck:        call_kind = LocationSummary::kCallOnSlowPath;        break; +    case TypeCheckKind::kBitstringCheck: +      break;    }    LocationSummary* locations = @@ -7423,7 +7473,13 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {      locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.    }    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    // The output does overlap inputs.    // Note that TypeCheckSlowPathMIPS uses this register too.    locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7435,7 +7491,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    Register obj = obj_loc.AsRegister<Register>(); -  Register cls = locations->InAt(1).AsRegister<Register>(); +  Location cls = locations->InAt(1);    Location out_loc = locations->Out();    Register out = out_loc.AsRegister<Register>();    const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7467,7 +7523,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {                                          maybe_temp_loc,                                          read_barrier_option);        // Classes must be equal for the instanceof to succeed. -      __ Xor(out, out, cls); +      __ Xor(out, out, cls.AsRegister<Register>());        __ Sltiu(out, out, 1);        break;      } @@ -7494,7 +7550,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {                                         read_barrier_option);        // If `out` is null, we use it for the result, and jump to `done`.        __ Beqz(out, &done); -      __ Bne(out, cls, &loop); +      __ Bne(out, cls.AsRegister<Register>(), &loop);        __ LoadConst32(out, 1);        break;      } @@ -7512,7 +7568,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {        // Walk over the class hierarchy to find a match.        MipsLabel loop, success;        __ Bind(&loop); -      __ Beq(out, cls, &success); +      __ Beq(out, cls.AsRegister<Register>(), &success);        // /* HeapReference<Class> */ out = out->super_class_        GenerateReferenceLoadOneRegister(instruction,                                         out_loc, @@ -7539,7 +7595,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {                                          read_barrier_option);        // Do an exact check.        MipsLabel success; -      __ Beq(out, cls, &success); +      __ Beq(out, cls.AsRegister<Register>(), &success);        // Otherwise, we need to check that the object's class is a non-primitive array.        // /* HeapReference<Class> */ out = out->component_type_        GenerateReferenceLoadOneRegister(instruction, @@ -7571,7 +7627,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {        slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(            instruction, /* is_fatal */ false);        codegen_->AddSlowPath(slow_path); -      __ Bne(out, cls, slow_path->GetEntryLabel()); +      __ Bne(out, cls.AsRegister<Register>(), slow_path->GetEntryLabel());        __ LoadConst32(out, 1);        break;      } @@ -7603,6 +7659,20 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {        __ B(slow_path->GetEntryLabel());        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        out_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, out); +      __ Sltiu(out, out, 1); +      break; +    }    }    __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 1f1743ff9e..2e7c736dbd 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -237,6 +237,7 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator {   private:    void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg);    void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); +  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp);    void HandleBinaryOp(HBinaryOperation* operation);    void HandleCondition(HCondition* instruction);    void HandleShift(HBinaryOperation* operation); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index f8851b4eea..5b07b55cbb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1794,6 +1794,34 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo    __ Bind(slow_path->GetExitLabel());  } +void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, +                                                                       GpuRegister temp) { +  uint32_t path_to_root = check->GetBitstringPathToRoot(); +  uint32_t mask = check->GetBitstringMask(); +  DCHECK(IsPowerOfTwo(mask + 1)); +  size_t mask_bits = WhichPowerOf2(mask + 1); + +  if (mask_bits == 16u) { +    // Load only the bitstring part of the status word. +    __ LoadFromOffset( +        kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); +    // Compare the bitstring bits using XOR. +    __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); +  } else { +    // /* uint32_t */ temp = temp->status_ +    __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); +    // Compare the bitstring bits using XOR. +    if (IsUint<16>(path_to_root)) { +      __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); +    } else { +      __ LoadConst32(TMP, path_to_root); +      __ Xor(temp, temp, TMP); +    } +    // Shift out bits that do not contribute to the comparison. +    __ Sll(temp, temp, 32 - mask_bits); +  } +} +  void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {    __ Sync(0);  // only stype 0 is supported  } @@ -2854,7 +2882,13 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations =        new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));  } @@ -2863,7 +2897,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    GpuRegister obj = obj_loc.AsRegister<GpuRegister>(); -  GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>(); +  Location cls = locations->InAt(1);    Location temp_loc = locations->GetTemp(0);    GpuRegister temp = temp_loc.AsRegister<GpuRegister>();    const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -2902,7 +2936,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {                                          kWithoutReadBarrier);        // Jump to slow path for throwing the exception or doing a        // more involved array check. -      __ Bnec(temp, cls, slow_path->GetEntryLabel()); +      __ Bnec(temp, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());        break;      } @@ -2928,7 +2962,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {        // exception.        __ Beqzc(temp, slow_path->GetEntryLabel());        // Otherwise, compare the classes. -      __ Bnec(temp, cls, &loop); +      __ Bnec(temp, cls.AsRegister<GpuRegister>(), &loop);        break;      } @@ -2943,7 +2977,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {        // Walk over the class hierarchy to find a match.        Mips64Label loop;        __ Bind(&loop); -      __ Beqc(temp, cls, &done); +      __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);        // /* HeapReference<Class> */ temp = temp->super_class_        GenerateReferenceLoadOneRegister(instruction,                                         temp_loc, @@ -2966,7 +3000,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {                                          maybe_temp2_loc,                                          kWithoutReadBarrier);        // Do an exact check. -      __ Beqc(temp, cls, &done); +      __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);        // Otherwise, we need to check that the object's class is a non-primitive array.        // /* HeapReference<Class> */ temp = temp->component_type_        GenerateReferenceLoadOneRegister(instruction, @@ -3025,7 +3059,21 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {        __ Daddiu(temp, temp, 2 * kHeapReferenceSize);        __ Addiu(TMP, TMP, -2);        // Compare the classes and continue the loop if they do not match. -      __ Bnec(AT, cls, &loop); +      __ Bnec(AT, cls.AsRegister<GpuRegister>(), &loop); +      break; +    } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        temp_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp2_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, temp); +      __ Bnezc(temp, slow_path->GetEntryLabel());        break;      }    } @@ -5529,6 +5577,8 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {      case TypeCheckKind::kInterfaceCheck:        call_kind = LocationSummary::kCallOnSlowPath;        break; +    case TypeCheckKind::kBitstringCheck: +      break;    }    LocationSummary* locations = @@ -5537,7 +5587,13 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {      locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.    }    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    // The output does overlap inputs.    // Note that TypeCheckSlowPathMIPS64 uses this register too.    locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -5549,7 +5605,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    GpuRegister obj = obj_loc.AsRegister<GpuRegister>(); -  GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>(); +  Location cls = locations->InAt(1);    Location out_loc = locations->Out();    GpuRegister out = out_loc.AsRegister<GpuRegister>();    const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -5581,7 +5637,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {                                          maybe_temp_loc,                                          read_barrier_option);        // Classes must be equal for the instanceof to succeed. -      __ Xor(out, out, cls); +      __ Xor(out, out, cls.AsRegister<GpuRegister>());        __ Sltiu(out, out, 1);        break;      } @@ -5608,7 +5664,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {                                         read_barrier_option);        // If `out` is null, we use it for the result, and jump to `done`.        __ Beqzc(out, &done); -      __ Bnec(out, cls, &loop); +      __ Bnec(out, cls.AsRegister<GpuRegister>(), &loop);        __ LoadConst32(out, 1);        break;      } @@ -5626,7 +5682,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {        // Walk over the class hierarchy to find a match.        Mips64Label loop, success;        __ Bind(&loop); -      __ Beqc(out, cls, &success); +      __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);        // /* HeapReference<Class> */ out = out->super_class_        GenerateReferenceLoadOneRegister(instruction,                                         out_loc, @@ -5653,7 +5709,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {                                          read_barrier_option);        // Do an exact check.        Mips64Label success; -      __ Beqc(out, cls, &success); +      __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);        // Otherwise, we need to check that the object's class is a non-primitive array.        // /* HeapReference<Class> */ out = out->component_type_        GenerateReferenceLoadOneRegister(instruction, @@ -5685,7 +5741,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {        slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64(            instruction, /* is_fatal */ false);        codegen_->AddSlowPath(slow_path); -      __ Bnec(out, cls, slow_path->GetEntryLabel()); +      __ Bnec(out, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());        __ LoadConst32(out, 1);        break;      } @@ -5717,6 +5773,20 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {        __ Bc(slow_path->GetEntryLabel());        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        out_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, out); +      __ Sltiu(out, out, 1); +      break; +    }    }    __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 74c947e5d5..6e69e4611a 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -233,6 +233,7 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator {   private:    void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg); +  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp);    void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);    void HandleBinaryOp(HBinaryOperation* operation);    void HandleCondition(HCondition* instruction); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4818084a72..4053f557d9 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6571,6 +6571,26 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck(    // No need for memory fence, thanks to the X86 memory model.  } +void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, +                                                                    Register temp) { +  uint32_t path_to_root = check->GetBitstringPathToRoot(); +  uint32_t mask = check->GetBitstringMask(); +  DCHECK(IsPowerOfTwo(mask + 1)); +  size_t mask_bits = WhichPowerOf2(mask + 1); + +  if (mask_bits == 16u) { +    // Compare the bitstring in memory. +    __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); +  } else { +    // /* uint32_t */ temp = temp->status_ +    __ movl(temp, Address(temp, mirror::Class::StatusOffset())); +    // Compare the bitstring bits using SUB. +    __ subl(temp, Immediate(path_to_root)); +    // Shift out bits that do not contribute to the comparison. +    __ shll(temp, Immediate(32u - mask_bits)); +  } +} +  HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(      HLoadString::LoadKind desired_string_load_kind) {    switch (desired_string_load_kind) { @@ -6764,6 +6784,8 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) {      case TypeCheckKind::kInterfaceCheck:        call_kind = LocationSummary::kCallOnSlowPath;        break; +    case TypeCheckKind::kBitstringCheck: +      break;    }    LocationSummary* locations = @@ -6772,7 +6794,13 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) {      locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.    }    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::Any()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::Any()); +  }    // Note that TypeCheckSlowPathX86 uses this "out" register too.    locations->SetOut(Location::RequiresRegister());    // When read barriers are enabled, we need a temporary register for some cases. @@ -6993,6 +7021,21 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) {        }        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        out_loc, +                                        obj_loc, +                                        class_offset, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, out); +      __ j(kNotEqual, &zero); +      __ movl(out, Immediate(1)); +      __ jmp(&done); +      break; +    }    }    if (zero.IsLinked()) { @@ -7019,6 +7062,10 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) {      // Require a register for the interface check since there is a loop that compares the class to      // a memory address.      locations->SetInAt(1, Location::RequiresRegister()); +  } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));    } else {      locations->SetInAt(1, Location::Any());    } @@ -7238,6 +7285,19 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) {        __ MaybeUnpoisonHeapReference(cls.AsRegister<Register>());        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        temp_loc, +                                        obj_loc, +                                        class_offset, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, temp); +      __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); +      break; +    }    }    __ Bind(&done); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 9c537a7371..6c76e27d35 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -211,6 +211,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator {    // the suspend call.    void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);    void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg); +  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp);    void HandleBitwiseOperation(HBinaryOperation* instruction);    void GenerateDivRemIntegral(HBinaryOperation* instruction);    void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index c378c5b957..496d79d6c8 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5716,6 +5716,26 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck(    // No need for memory fence, thanks to the x86-64 memory model.  } +void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, +                                                                       CpuRegister temp) { +  uint32_t path_to_root = check->GetBitstringPathToRoot(); +  uint32_t mask = check->GetBitstringMask(); +  DCHECK(IsPowerOfTwo(mask + 1)); +  size_t mask_bits = WhichPowerOf2(mask + 1); + +  if (mask_bits == 16u) { +    // Compare the bitstring in memory. +    __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); +  } else { +    // /* uint32_t */ temp = temp->status_ +    __ movl(temp, Address(temp, mirror::Class::StatusOffset())); +    // Compare the bitstring bits using SUB. +    __ subl(temp, Immediate(path_to_root)); +    // Shift out bits that do not contribute to the comparison. +    __ shll(temp, Immediate(32u - mask_bits)); +  } +} +  HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(      HLoadClass::LoadKind desired_class_load_kind) {    switch (desired_class_load_kind) { @@ -6082,6 +6102,8 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {      case TypeCheckKind::kInterfaceCheck:        call_kind = LocationSummary::kCallOnSlowPath;        break; +    case TypeCheckKind::kBitstringCheck: +      break;    }    LocationSummary* locations = @@ -6090,7 +6112,13 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {      locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.    }    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::Any()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::Any()); +  }    // Note that TypeCheckSlowPathX86_64 uses this "out" register too.    locations->SetOut(Location::RequiresRegister());    // When read barriers are enabled, we need a temporary register for @@ -6319,6 +6347,27 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {        }        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        out_loc, +                                        obj_loc, +                                        class_offset, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, out); +      if (zero.IsLinked()) { +        __ j(kNotEqual, &zero); +        __ movl(out, Immediate(1)); +        __ jmp(&done); +      } else { +        __ setcc(kEqual, out); +        // setcc only sets the low byte. +        __ andl(out, Immediate(1)); +      } +      break; +    }    }    if (zero.IsLinked()) { @@ -6345,6 +6394,10 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) {      // Require a register for the interface check since there is a loop that compares the class to      // a memory address.      locations->SetInAt(1, Location::RequiresRegister()); +  } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));    } else {      locations->SetInAt(1, Location::Any());    } @@ -6531,7 +6584,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {        break;      } -    case TypeCheckKind::kInterfaceCheck: +    case TypeCheckKind::kInterfaceCheck: {        // Fast path for the interface check. Try to avoid read barriers to improve the fast path.        // We can not get false positives by doing this.        // /* HeapReference<Class> */ temp = obj->klass_ @@ -6567,6 +6620,20 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {        // If `cls` was poisoned above, unpoison it.        __ MaybeUnpoisonHeapReference(cls.AsRegister<CpuRegister>());        break; +    } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        temp_loc, +                                        obj_loc, +                                        class_offset, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, temp); +      __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); +      break; +    }    }    if (done.IsLinked()) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index e8d1efe702..9a4c53b524 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -208,6 +208,7 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator {    // the suspend call.    void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);    void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg); +  void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp);    void HandleBitwiseOperation(HBinaryOperation* operation);    void GenerateRemFP(HRem* rem);    void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index c88baa8610..fbcbe3608e 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -25,6 +25,11 @@  #include "base/bit_vector-inl.h"  #include "base/scoped_arena_allocator.h"  #include "base/scoped_arena_containers.h" +#include "handle.h" +#include "mirror/class.h" +#include "obj_ptr-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "subtype_check.h"  namespace art { @@ -548,30 +553,85 @@ void GraphChecker::VisitReturnVoid(HReturnVoid* ret) {    }  } -void GraphChecker::VisitCheckCast(HCheckCast* check) { -  VisitInstruction(check); -  HInstruction* input = check->InputAt(1); -  if (!input->IsLoadClass()) { -    AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", +void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, +                                                size_t input_pos, +                                                bool check_value, +                                                uint32_t expected_value, +                                                const char* name) { +  if (!check->InputAt(input_pos)->IsIntConstant()) { +    AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.",                            check->DebugName(),                            check->GetId(), -                          input->DebugName(), -                          input->GetId())); +                          input_pos, +                          name, +                          check->InputAt(2)->DebugName(), +                          check->InputAt(2)->GetId())); +  } else if (check_value) { +    uint32_t actual_value = +        static_cast<uint32_t>(check->InputAt(input_pos)->AsIntConstant()->GetValue()); +    if (actual_value != expected_value) { +      AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.", +                            check->DebugName(), +                            check->GetId(), +                            name, +                            actual_value, +                            expected_value)); +    }    }  } -void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { -  VisitInstruction(instruction); -  HInstruction* input = instruction->InputAt(1); -  if (!input->IsLoadClass()) { -    AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", -                          instruction->DebugName(), -                          instruction->GetId(), -                          input->DebugName(), -                          input->GetId())); +void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) { +  VisitInstruction(check); +  HInstruction* input = check->InputAt(1); +  if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { +    if (!input->IsNullConstant()) { +      AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.", +                            check->DebugName(), +                            check->GetId(), +                            input->DebugName(), +                            input->GetId())); +    } +    bool check_values = false; +    BitString::StorageType expected_path_to_root = 0u; +    BitString::StorageType expected_mask = 0u; +    { +      ScopedObjectAccess soa(Thread::Current()); +      ObjPtr<mirror::Class> klass = check->GetClass().Get(); +      MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); +      SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass); +      if (state == SubtypeCheckInfo::kAssigned) { +        expected_path_to_root = +            SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass); +        expected_mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass); +        check_values = true; +      } else { +        AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.", +                              check->DebugName(), +                              check->GetId())); +      } +    } +    CheckTypeCheckBitstringInput( +        check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root"); +    CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask"); +  } else { +    if (!input->IsLoadClass()) { +      AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.", +                            check->DebugName(), +                            check->GetId(), +                            input->DebugName(), +                            input->GetId())); +    }    }  } +void GraphChecker::VisitCheckCast(HCheckCast* check) { +  HandleTypeCheckInstruction(check); +} + +void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { +  HandleTypeCheckInstruction(instruction); +} +  void GraphChecker::HandleLoop(HBasicBlock* loop_header) {    int id = loop_header->GetBlockId();    HLoopInformation* loop_information = loop_header->GetLoopInformation(); diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 0f0b49d240..dbedc40518 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -71,6 +71,12 @@ class GraphChecker : public HGraphDelegateVisitor {    void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE;    void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; +  void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, +                                    size_t input_pos, +                                    bool check_value, +                                    uint32_t expected_value, +                                    const char* name); +  void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction);    void HandleLoop(HBasicBlock* loop_header);    void HandleBooleanInput(HInstruction* instruction, size_t input_index); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 5ff31cead5..54d4644580 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -390,16 +390,23 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {      StartAttributeStream("load_kind") << load_string->GetLoadKind();    } -  void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { -    StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind(); +  void HandleTypeCheckInstruction(HTypeCheckInstruction* check) { +    StartAttributeStream("check_kind") << check->GetTypeCheckKind();      StartAttributeStream("must_do_null_check") << std::boolalpha -        << check_cast->MustDoNullCheck() << std::noboolalpha; +        << check->MustDoNullCheck() << std::noboolalpha; +    if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { +      StartAttributeStream("path_to_root") << std::hex +          << "0x" << check->GetBitstringPathToRoot() << std::dec; +      StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec; +    } +  } + +  void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { +    HandleTypeCheckInstruction(check_cast);    }    void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE { -    StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind(); -    StartAttributeStream("must_do_null_check") << std::boolalpha -        << instance_of->MustDoNullCheck() << std::noboolalpha; +    HandleTypeCheckInstruction(instance_of);    }    void VisitArrayLength(HArrayLength* array_length) OVERRIDE { @@ -576,6 +583,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {        }        StartAttributeStream() << input_list;      } +    if (instruction->GetDexPc() != kNoDexPc) { +      StartAttributeStream("dex_pc") << instruction->GetDexPc(); +    } else { +      StartAttributeStream("dex_pc") << "n/a"; +    }      instruction->Accept(this);      if (instruction->HasEnvironment()) {        StringList envs; @@ -641,20 +653,32 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {            << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha;      } +    // For the builder and the inliner, we want to add extra information on HInstructions +    // that have reference types, and also HInstanceOf/HCheckcast.      if ((IsPass(HGraphBuilder::kBuilderPassName)          || IsPass(HInliner::kInlinerPassName)) -        && (instruction->GetType() == DataType::Type::kReference)) { -      ReferenceTypeInfo info = instruction->IsLoadClass() -        ? instruction->AsLoadClass()->GetLoadedClassRTI() -        : instruction->GetReferenceTypeInfo(); +        && (instruction->GetType() == DataType::Type::kReference || +            instruction->IsInstanceOf() || +            instruction->IsCheckCast())) { +      ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference) +          ? instruction->IsLoadClass() +              ? instruction->AsLoadClass()->GetLoadedClassRTI() +              : instruction->GetReferenceTypeInfo() +          : instruction->IsInstanceOf() +              ? instruction->AsInstanceOf()->GetTargetClassRTI() +              : instruction->AsCheckCast()->GetTargetClassRTI();        ScopedObjectAccess soa(Thread::Current());        if (info.IsValid()) {          StartAttributeStream("klass")              << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get()); -        StartAttributeStream("can_be_null") -            << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; +        if (instruction->GetType() == DataType::Type::kReference) { +          StartAttributeStream("can_be_null") +              << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; +        }          StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; -      } else if (instruction->IsLoadClass()) { +      } else if (instruction->IsLoadClass() || +                 instruction->IsInstanceOf() || +                 instruction->IsCheckCast()) {          StartAttributeStream("klass") << "unresolved";        } else {          // The NullConstant may be added to the graph during other passes that happen between diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 0a310ca940..55eca2316a 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -352,13 +352,15 @@ void InductionVarRange::Replace(HInstruction* instruction,  }  bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const { -  HInductionVarAnalysis::InductionInfo *trip = -      induction_analysis_->LookupInfo(loop, GetLoopControl(loop)); -  if (trip != nullptr && !IsUnsafeTripCount(trip)) { -    IsConstant(trip->op_a, kExact, trip_count); -    return true; -  } -  return false; +  bool is_constant_unused = false; +  return CheckForFiniteAndConstantProps(loop, &is_constant_unused, trip_count); +} + +bool InductionVarRange::HasKnownTripCount(HLoopInformation* loop, +                                          /*out*/ int64_t* trip_count) const { +  bool is_constant = false; +  CheckForFiniteAndConstantProps(loop, &is_constant, trip_count); +  return is_constant;  }  bool InductionVarRange::IsUnitStride(HInstruction* context, @@ -417,6 +419,18 @@ HInstruction* InductionVarRange::GenerateTripCount(HLoopInformation* loop,  // Private class methods.  // +bool InductionVarRange::CheckForFiniteAndConstantProps(HLoopInformation* loop, +                                                       /*out*/ bool* is_constant, +                                                       /*out*/ int64_t* trip_count) const { +  HInductionVarAnalysis::InductionInfo *trip = +      induction_analysis_->LookupInfo(loop, GetLoopControl(loop)); +  if (trip != nullptr && !IsUnsafeTripCount(trip)) { +    *is_constant = IsConstant(trip->op_a, kExact, trip_count); +    return true; +  } +  return false; +} +  bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info,                                     ConstantRequest request,                                     /*out*/ int64_t* value) const { diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index 0b980f596a..906dc6bb7b 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -161,9 +161,15 @@ class InductionVarRange {    }    /** -   * Checks if header logic of a loop terminates. Sets trip-count tc if known. +   * Checks if header logic of a loop terminates. If trip count is known sets 'trip_count' to its +   * value.     */ -  bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const; +  bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const; + +  /** +   * Checks if a trip count is known for the loop and sets 'trip_count' to its value in this case. +   */ +  bool HasKnownTripCount(HLoopInformation* loop, /*out*/ int64_t* trip_count) const;    /**     * Checks if the given instruction is a unit stride induction inside the closest enveloping @@ -194,6 +200,14 @@ class InductionVarRange {    };    /** +   * Checks if header logic of a loop terminates. If trip count is known (constant) sets +   * 'is_constant' to true and 'trip_count' to the trip count value. +   */ +  bool CheckForFiniteAndConstantProps(HLoopInformation* loop, +                                      /*out*/ bool* is_constant, +                                      /*out*/ int64_t* trip_count) const; + +  /**     * Returns true if exact or upper/lower bound on the given induction     * information is known as a 64-bit constant, which is returned in value.     */ diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c7aef3779d..9647dd5d41 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1815,29 +1815,6 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object,    }  } -static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) -    REQUIRES_SHARED(Locks::mutator_lock_) { -  if (cls == nullptr) { -    return TypeCheckKind::kUnresolvedCheck; -  } else if (cls->IsInterface()) { -    return TypeCheckKind::kInterfaceCheck; -  } else if (cls->IsArrayClass()) { -    if (cls->GetComponentType()->IsObjectClass()) { -      return TypeCheckKind::kArrayObjectCheck; -    } else if (cls->CannotBeAssignedFromOtherTypes()) { -      return TypeCheckKind::kExactCheck; -    } else { -      return TypeCheckKind::kArrayCheck; -    } -  } else if (cls->IsFinal()) { -    return TypeCheckKind::kExactCheck; -  } else if (cls->IsAbstract()) { -    return TypeCheckKind::kAbstractClassCheck; -  } else { -    return TypeCheckKind::kClassHierarchyCheck; -  } -} -  void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) {    HLoadString* load_string =        new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc); @@ -1852,22 +1829,8 @@ void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_  HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) {    ScopedObjectAccess soa(Thread::Current());    const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); -  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); -  Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass( -      soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); - -  bool needs_access_check = true; -  if (klass != nullptr) { -    if (klass->IsPublic()) { -      needs_access_check = false; -    } else { -      ObjPtr<mirror::Class> compiling_class = GetCompilingClass(); -      if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { -        needs_access_check = false; -      } -    } -  } - +  Handle<mirror::Class> klass = ResolveClass(soa, type_index); +  bool needs_access_check = LoadClassNeedsAccessCheck(klass);    return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check);  } @@ -1912,25 +1875,83 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index,    return load_class;  } +Handle<mirror::Class> HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa, +                                                        dex::TypeIndex type_index) { +  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); +  ObjPtr<mirror::Class> klass = compiler_driver_->ResolveClass( +      soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_); +  // TODO: Avoid creating excessive handles if the method references the same class repeatedly. +  // (Use a map on the local_allocator_.) +  return handles_->NewHandle(klass); +} + +bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) { +  if (klass == nullptr) { +    return true; +  } else if (klass->IsPublic()) { +    return false; +  } else { +    ObjPtr<mirror::Class> compiling_class = GetCompilingClass(); +    return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get()); +  } +} +  void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,                                           uint8_t destination,                                           uint8_t reference,                                           dex::TypeIndex type_index,                                           uint32_t dex_pc) {    HInstruction* object = LoadLocal(reference, DataType::Type::kReference); -  HLoadClass* cls = BuildLoadClass(type_index, dex_pc);    ScopedObjectAccess soa(Thread::Current()); -  TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass()); +  const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); +  Handle<mirror::Class> klass = ResolveClass(soa, type_index); +  bool needs_access_check = LoadClassNeedsAccessCheck(klass); +  TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind( +      klass.Get(), code_generator_, compiler_driver_, needs_access_check); + +  HInstruction* class_or_null = nullptr; +  HIntConstant* bitstring_path_to_root = nullptr; +  HIntConstant* bitstring_mask = nullptr; +  if (check_kind == TypeCheckKind::kBitstringCheck) { +    // TODO: Allow using the bitstring check also if we need an access check. +    DCHECK(!needs_access_check); +    class_or_null = graph_->GetNullConstant(dex_pc); +    MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); +    uint32_t path_to_root = +        SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass.Get()); +    uint32_t mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass.Get()); +    bitstring_path_to_root = graph_->GetIntConstant(static_cast<int32_t>(path_to_root), dex_pc); +    bitstring_mask = graph_->GetIntConstant(static_cast<int32_t>(mask), dex_pc); +  } else { +    class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); +  } +  DCHECK(class_or_null != nullptr); +    if (instruction.Opcode() == Instruction::INSTANCE_OF) { -    AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc)); +    AppendInstruction(new (allocator_) HInstanceOf(object, +                                                   class_or_null, +                                                   check_kind, +                                                   klass, +                                                   dex_pc, +                                                   allocator_, +                                                   bitstring_path_to_root, +                                                   bitstring_mask));      UpdateLocal(destination, current_block_->GetLastInstruction());    } else {      DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);      // We emit a CheckCast followed by a BoundType. CheckCast is a statement      // which may throw. If it succeeds BoundType sets the new type of `object`      // for all subsequent uses. -    AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc)); +    AppendInstruction( +        new (allocator_) HCheckCast(object, +                                    class_or_null, +                                    check_kind, +                                    klass, +                                    dex_pc, +                                    allocator_, +                                    bitstring_path_to_root, +                                    bitstring_mask));      AppendInstruction(new (allocator_) HBoundType(object, dex_pc));      UpdateLocal(reference, current_block_->GetLastInstruction());    } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 4428c53277..f78829232d 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -39,6 +39,7 @@ class DexCompilationUnit;  class HBasicBlockBuilder;  class Instruction;  class OptimizingCompilerStats; +class ScopedObjectAccess;  class SsaBuilder;  class VariableSizedHandleScope; @@ -232,6 +233,12 @@ class HInstructionBuilder : public ValueObject {                               bool needs_access_check)        REQUIRES_SHARED(Locks::mutator_lock_); +  Handle<mirror::Class> ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index) +      REQUIRES_SHARED(Locks::mutator_lock_); + +  bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) +      REQUIRES_SHARED(Locks::mutator_lock_); +    // Returns the outer-most compiling method's class.    ObjPtr<mirror::Class> GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 0b2297d157..676fe6bcb7 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -579,7 +579,9 @@ bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInst  // Returns whether doing a type test between the class of `object` against `klass` has  // a statically known outcome. The result of the test is stored in `outcome`. -static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) { +static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, +                                     HInstruction* object, +                                     /*out*/bool* outcome) {    DCHECK(!object->IsNullConstant()) << "Null constants should be special cased";    ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();    ScopedObjectAccess soa(Thread::Current()); @@ -589,7 +591,6 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo      return false;    } -  ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();    if (!class_rti.IsValid()) {      // Happens when the loaded class is unresolved.      return false; @@ -614,8 +615,8 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo  void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {    HInstruction* object = check_cast->InputAt(0); -  HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); -  if (load_class->NeedsAccessCheck()) { +  if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && +      check_cast->GetTargetClass()->NeedsAccessCheck()) {      // If we need to perform an access check we cannot remove the instruction.      return;    } @@ -633,15 +634,18 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {    // Note: The `outcome` is initialized to please valgrind - the compiler can reorder    // the return value check with the `outcome` check, b/27651442 .    bool outcome = false; -  if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { +  if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) {      if (outcome) {        check_cast->GetBlock()->RemoveInstruction(check_cast);        MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast); -      if (!load_class->HasUses()) { -        // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. -        // However, here we know that it cannot because the checkcast was successfull, hence -        // the class was already loaded. -        load_class->GetBlock()->RemoveInstruction(load_class); +      if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { +        HLoadClass* load_class = check_cast->GetTargetClass(); +        if (!load_class->HasUses()) { +          // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. +          // However, here we know that it cannot because the checkcast was successfull, hence +          // the class was already loaded. +          load_class->GetBlock()->RemoveInstruction(load_class); +        }        }      } else {        // Don't do anything for exceptional cases for now. Ideally we should remove @@ -652,8 +656,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {  void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {    HInstruction* object = instruction->InputAt(0); -  HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass(); -  if (load_class->NeedsAccessCheck()) { +  if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && +      instruction->GetTargetClass()->NeedsAccessCheck()) {      // If we need to perform an access check we cannot remove the instruction.      return;    } @@ -676,7 +680,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {    // Note: The `outcome` is initialized to please valgrind - the compiler can reorder    // the return value check with the `outcome` check, b/27651442 .    bool outcome = false; -  if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { +  if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) {      MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf);      if (outcome && can_be_null) {        // Type test will succeed, we just need a null test. @@ -689,11 +693,14 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {      }      RecordSimplification();      instruction->GetBlock()->RemoveInstruction(instruction); -    if (outcome && !load_class->HasUses()) { -      // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. -      // However, here we know that it cannot because the instanceof check was successfull, hence -      // the class was already loaded. -      load_class->GetBlock()->RemoveInstruction(load_class); +    if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { +      HLoadClass* load_class = instruction->GetTargetClass(); +      if (!load_class->HasUses()) { +        // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. +        // However, here we know that it cannot because the instanceof check was successfull, hence +        // the class was already loaded. +        load_class->GetBlock()->RemoveInstruction(load_class); +      }      }    }  } diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc new file mode 100644 index 0000000000..cd3bdaf016 --- /dev/null +++ b/compiler/optimizing/loop_analysis.cc @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "loop_analysis.h" + +namespace art { + +void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info, +                                                LoopAnalysisInfo* analysis_results) { +  for (HBlocksInLoopIterator block_it(*loop_info); +       !block_it.Done(); +       block_it.Advance()) { +    HBasicBlock* block = block_it.Current(); + +    for (HBasicBlock* successor : block->GetSuccessors()) { +      if (!loop_info->Contains(*successor)) { +        analysis_results->exits_num_++; +      } +    } + +    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { +      HInstruction* instruction = it.Current(); +      if (MakesScalarUnrollingNonBeneficial(instruction)) { +        analysis_results->has_instructions_preventing_scalar_unrolling_ = true; +      } +      analysis_results->instr_num_++; +    } +    analysis_results->bb_num_++; +  } +} + +class Arm64LoopHelper : public ArchDefaultLoopHelper { + public: +  // Scalar loop unrolling parameters and heuristics. +  // +  // Maximum possible unrolling factor. +  static constexpr uint32_t kArm64ScalarMaxUnrollFactor = 2; +  // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled. +  static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeInstr = 40; +  // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled. +  static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeBlocks = 8; + +  // SIMD loop unrolling parameters and heuristics. +  // +  // Maximum possible unrolling factor. +  static constexpr uint32_t kArm64SimdMaxUnrollFactor = 8; +  // Loop's maximum instruction count. Loops with higher count will not be unrolled. +  static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50; + +  bool IsLoopTooBigForScalarUnrolling(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { +    size_t instr_num = loop_analysis_info->GetNumberOfInstructions(); +    size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks(); +    return (instr_num >= kArm64ScalarHeuristicMaxBodySizeInstr || +            bb_num >= kArm64ScalarHeuristicMaxBodySizeBlocks); +  } + +  uint32_t GetScalarUnrollingFactor(HLoopInformation* loop_info ATTRIBUTE_UNUSED, +                                    uint64_t trip_count) const OVERRIDE { +    uint32_t desired_unrolling_factor = kArm64ScalarMaxUnrollFactor; +    if (trip_count < desired_unrolling_factor || trip_count % desired_unrolling_factor != 0) { +      return kNoUnrollingFactor; +    } + +    return desired_unrolling_factor; +  } + +  uint32_t GetSIMDUnrollingFactor(HBasicBlock* block, +                                  int64_t trip_count, +                                  uint32_t max_peel, +                                  uint32_t vector_length) const OVERRIDE { +    // Don't unroll with insufficient iterations. +    // TODO: Unroll loops with unknown trip count. +    DCHECK_NE(vector_length, 0u); +    if (trip_count < (2 * vector_length + max_peel)) { +      return kNoUnrollingFactor; +    } +    // Don't unroll for large loop body size. +    uint32_t instruction_count = block->GetInstructions().CountSize(); +    if (instruction_count >= kArm64SimdHeuristicMaxBodySizeInstr) { +      return kNoUnrollingFactor; +    } +    // Find a beneficial unroll factor with the following restrictions: +    //  - At least one iteration of the transformed loop should be executed. +    //  - The loop body shouldn't be "too big" (heuristic). + +    uint32_t uf1 = kArm64SimdHeuristicMaxBodySizeInstr / instruction_count; +    uint32_t uf2 = (trip_count - max_peel) / vector_length; +    uint32_t unroll_factor = +        TruncToPowerOfTwo(std::min({uf1, uf2, kArm64SimdMaxUnrollFactor})); +    DCHECK_GE(unroll_factor, 1u); +    return unroll_factor; +  } +}; + +ArchDefaultLoopHelper* ArchDefaultLoopHelper::Create(InstructionSet isa, +                                                     ArenaAllocator* allocator) { +  switch (isa) { +    case InstructionSet::kArm64: { +      return new (allocator) Arm64LoopHelper; +    } +    default: { +      return new (allocator) ArchDefaultLoopHelper; +    } +  } +} + +}  // namespace art diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h new file mode 100644 index 0000000000..bad406f10b --- /dev/null +++ b/compiler/optimizing/loop_analysis.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ +#define ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ + +#include "nodes.h" + +namespace art { + +class LoopAnalysis; + +// No loop unrolling factor (just one copy of the loop-body). +static constexpr uint32_t kNoUnrollingFactor = 1; + +// Class to hold cached information on properties of the loop. +class LoopAnalysisInfo : public ValueObject { + public: +  explicit LoopAnalysisInfo(HLoopInformation* loop_info) +      : bb_num_(0), +        instr_num_(0), +        exits_num_(0), +        has_instructions_preventing_scalar_unrolling_(false), +        loop_info_(loop_info) {} + +  size_t GetNumberOfBasicBlocks() const { return bb_num_; } +  size_t GetNumberOfInstructions() const { return instr_num_; } +  size_t GetNumberOfExits() const { return exits_num_; } + +  bool HasInstructionsPreventingScalarUnrolling() const { +    return has_instructions_preventing_scalar_unrolling_; +  } + +  const HLoopInformation* GetLoopInfo() const { return loop_info_; } + + private: +  // Number of basic blocks in the loop body. +  size_t bb_num_; +  // Number of instructions in the loop body. +  size_t instr_num_; +  // Number of loop's exits. +  size_t exits_num_; +  // Whether the loop has instructions which make scalar loop unrolling non-beneficial. +  bool has_instructions_preventing_scalar_unrolling_; + +  // Corresponding HLoopInformation. +  const HLoopInformation* loop_info_; + +  friend class LoopAnalysis; +}; + +// Placeholder class for methods and routines used to analyse loops, calculate loop properties +// and characteristics. +class LoopAnalysis : public ValueObject { + public: +  // Calculates loops basic properties like body size, exits number, etc. and fills +  // 'analysis_results' with this information. +  static void CalculateLoopBasicProperties(HLoopInformation* loop_info, +                                           LoopAnalysisInfo* analysis_results); + + private: +  // Returns whether an instruction makes scalar loop unrolling non-beneficial. +  // +  // If in the loop body we have a dex/runtime call then its contribution to the whole +  // loop performance will probably prevail. So unrolling optimization will not bring +  // any noticeable performance improvement however will increase the code size. +  static bool MakesScalarUnrollingNonBeneficial(HInstruction* instruction) { +    return (instruction->IsNewArray() || +        instruction->IsNewInstance() || +        instruction->IsUnresolvedInstanceFieldGet() || +        instruction->IsUnresolvedInstanceFieldSet() || +        instruction->IsUnresolvedStaticFieldGet() || +        instruction->IsUnresolvedStaticFieldSet() || +        // TODO: Unroll loops with intrinsified invokes. +        instruction->IsInvoke() || +        // TODO: Unroll loops with ClinitChecks. +        instruction->IsClinitCheck()); +  } +}; + +// +// Helper class which holds target-dependent methods and constants needed for loop optimizations. +// +// To support peeling/unrolling for a new architecture one needs to create new helper class, +// inherit it from this and add implementation for the following methods. +// +class ArchDefaultLoopHelper : public ArenaObject<kArenaAllocOptimization> { + public: +  virtual ~ArchDefaultLoopHelper() {} + +  // Creates an instance of specialised helper for the target or default helper if the target +  // doesn't support loop peeling and unrolling. +  static ArchDefaultLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator); + +  // Returns whether the loop is too big for loop unrolling by checking its total number of +  // basic blocks and instructions. +  // +  // If the loop body has too many instructions then unrolling optimization will not bring +  // any noticeable performance improvement however will increase the code size. +  // +  // Returns 'true' by default, should be overridden by particular target loop helper. +  virtual bool IsLoopTooBigForScalarUnrolling( +      LoopAnalysisInfo* loop_analysis_info ATTRIBUTE_UNUSED) const { return true; } + +  // Returns optimal scalar unrolling factor for the loop. +  // +  // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. +  virtual uint32_t GetScalarUnrollingFactor(HLoopInformation* loop_info ATTRIBUTE_UNUSED, +                                            uint64_t trip_count ATTRIBUTE_UNUSED) const { +    return kNoUnrollingFactor; +  } + +  // Returns optimal SIMD unrolling factor for the loop. +  // +  // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. +  virtual uint32_t GetSIMDUnrollingFactor(HBasicBlock* block ATTRIBUTE_UNUSED, +                                          int64_t trip_count ATTRIBUTE_UNUSED, +                                          uint32_t max_peel ATTRIBUTE_UNUSED, +                                          uint32_t vector_length ATTRIBUTE_UNUSED) const { +    return kNoUnrollingFactor; +  } +}; + +}  // namespace art + +#endif  // ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 758aca2d0c..71e24de141 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -33,8 +33,8 @@ namespace art {  // Enables vectorization (SIMDization) in the loop optimizer.  static constexpr bool kEnableVectorization = true; -// No loop unrolling factor (just one copy of the loop-body). -static constexpr uint32_t kNoUnrollingFactor = 1; +// Enables scalar loop unrolling in the loop optimizer. +static constexpr bool kEnableScalarUnrolling = false;  //  // Static helpers. @@ -227,6 +227,7 @@ static bool IsNarrowerOperands(HInstruction* a,                                 /*out*/ HInstruction** r,                                 /*out*/ HInstruction** s,                                 /*out*/ bool* is_unsigned) { +  DCHECK(a != nullptr && b != nullptr);    // Look for a matching sign extension.    DataType::Type stype = HVecOperation::ToSignedType(type);    if (IsSignExtensionAndGet(a, stype, r) && IsSignExtensionAndGet(b, stype, s)) { @@ -247,6 +248,7 @@ static bool IsNarrowerOperand(HInstruction* a,                                DataType::Type type,                                /*out*/ HInstruction** r,                                /*out*/ bool* is_unsigned) { +  DCHECK(a != nullptr);    // Look for a matching sign extension.    DataType::Type stype = HVecOperation::ToSignedType(type);    if (IsSignExtensionAndGet(a, stype, r)) { @@ -270,20 +272,28 @@ static uint32_t GetOtherVL(DataType::Type other_type, DataType::Type vector_type    return vl >> (DataType::SizeShift(other_type) - DataType::SizeShift(vector_type));  } -// Detect up to two instructions a and b, and an acccumulated constant c. -static bool IsAddConstHelper(HInstruction* instruction, -                             /*out*/ HInstruction** a, -                             /*out*/ HInstruction** b, -                             /*out*/ int64_t* c, -                             int32_t depth) { -  static constexpr int32_t kMaxDepth = 8;  // don't search too deep +// Detect up to two added operands a and b and an acccumulated constant c. +static bool IsAddConst(HInstruction* instruction, +                       /*out*/ HInstruction** a, +                       /*out*/ HInstruction** b, +                       /*out*/ int64_t* c, +                       int32_t depth = 8) {  // don't search too deep    int64_t value = 0; +  // Enter add/sub while still within reasonable depth. +  if (depth > 0) { +    if (instruction->IsAdd()) { +      return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1) && +             IsAddConst(instruction->InputAt(1), a, b, c, depth - 1); +    } else if (instruction->IsSub() && +               IsInt64AndGet(instruction->InputAt(1), &value)) { +      *c -= value; +      return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1); +    } +  } +  // Otherwise, deal with leaf nodes.    if (IsInt64AndGet(instruction, &value)) {      *c += value;      return true; -  } else if (instruction->IsAdd() && depth <= kMaxDepth) { -    return IsAddConstHelper(instruction->InputAt(0), a, b, c, depth + 1) && -           IsAddConstHelper(instruction->InputAt(1), a, b, c, depth + 1);    } else if (*a == nullptr) {      *a = instruction;      return true; @@ -291,42 +301,40 @@ static bool IsAddConstHelper(HInstruction* instruction,      *b = instruction;      return true;    } -  return false;  // too many non-const operands +  return false;  // too many operands  } -// Detect a + b + c for an optional constant c. -static bool IsAddConst(HInstruction* instruction, -                       /*out*/ HInstruction** a, -                       /*out*/ HInstruction** b, -                       /*out*/ int64_t* c) { -  if (instruction->IsAdd()) { -    // Try to find a + b and accumulated c. -    if (IsAddConstHelper(instruction->InputAt(0), a, b, c, /*depth*/ 0) && -        IsAddConstHelper(instruction->InputAt(1), a, b, c, /*depth*/ 0) && -        *b != nullptr) { -      return true; +// Detect a + b + c with optional constant c. +static bool IsAddConst2(HGraph* graph, +                        HInstruction* instruction, +                        /*out*/ HInstruction** a, +                        /*out*/ HInstruction** b, +                        /*out*/ int64_t* c) { +  if (IsAddConst(instruction, a, b, c) && *a != nullptr) { +    if (*b == nullptr) { +      // Constant is usually already present, unless accumulated. +      *b = graph->GetConstant(instruction->GetType(), (*c)); +      *c = 0;      } -    // Found a + b. -    *a = instruction->InputAt(0); -    *b = instruction->InputAt(1); -    *c = 0;      return true;    }    return false;  } -// Detect a + c for constant c. -static bool IsAddConst(HInstruction* instruction, -                       /*out*/ HInstruction** a, -                       /*out*/ int64_t* c) { -  if (instruction->IsAdd()) { -    if (IsInt64AndGet(instruction->InputAt(0), c)) { -      *a = instruction->InputAt(1); -      return true; -    } else if (IsInt64AndGet(instruction->InputAt(1), c)) { -      *a = instruction->InputAt(0); -      return true; -    } +// Detect a direct a - b or a hidden a - (-c). +static bool IsSubConst2(HGraph* graph, +                        HInstruction* instruction, +                        /*out*/ HInstruction** a, +                        /*out*/ HInstruction** b) { +  int64_t c = 0; +  if (instruction->IsSub()) { +    *a = instruction->InputAt(0); +    *b = instruction->InputAt(1); +    return true; +  } else if (IsAddConst(instruction, a, b, &c) && *a != nullptr && *b == nullptr) { +    // Constant for the hidden subtraction. +    *b = graph->GetConstant(instruction->GetType(), -c); +    return true;    }    return false;  } @@ -378,7 +386,8 @@ static bool CanSetRange(DataType::Type type,  }  // Accept various saturated addition forms. -static bool IsSaturatedAdd(HInstruction* clippee, +static bool IsSaturatedAdd(HInstruction* a, +                           HInstruction* b,                             DataType::Type type,                             int64_t lo,                             int64_t hi, @@ -390,8 +399,7 @@ static bool IsSaturatedAdd(HInstruction* clippee,    // Tighten the range for signed single clipping on constant.    if (!is_unsigned) {      int64_t c = 0; -    HInstruction* notused = nullptr; -    if (IsAddConst(clippee, ¬used, &c)) { +    if (IsInt64AndGet(a, &c) || IsInt64AndGet(b, &c)) {        // For c in proper range and narrower operand r:        //    MIN(r + c,  127) c > 0        // or MAX(r + c, -128) c < 0 (and possibly redundant bound). @@ -413,7 +421,7 @@ static bool IsSaturatedAdd(HInstruction* clippee,  }  // Accept various saturated subtraction forms. -static bool IsSaturatedSub(HInstruction* clippee, +static bool IsSaturatedSub(HInstruction* a,                             DataType::Type type,                             int64_t lo,                             int64_t hi, @@ -425,7 +433,7 @@ static bool IsSaturatedSub(HInstruction* clippee,    // Tighten the range for signed single clipping on constant.    if (!is_unsigned) {      int64_t c = 0; -    if (IsInt64AndGet(clippee->InputAt(0), /*out*/ &c)) { +    if (IsInt64AndGet(a, /*out*/ &c)) {        // For c in proper range and narrower operand r:        //    MIN(c - r,  127) c > 0        // or MAX(c - r, -128) c < 0 (and possibly redundant bound). @@ -532,7 +540,11 @@ HLoopOptimization::HLoopOptimization(HGraph* graph,        vector_preheader_(nullptr),        vector_header_(nullptr),        vector_body_(nullptr), -      vector_index_(nullptr) { +      vector_index_(nullptr), +      arch_loop_helper_(ArchDefaultLoopHelper::Create(compiler_driver_ != nullptr +                                                          ? compiler_driver_->GetInstructionSet() +                                                          : InstructionSet::kNone, +                                                      global_allocator_)) {  }  void HLoopOptimization::Run() { @@ -743,7 +755,7 @@ void HLoopOptimization::SimplifyBlocks(LoopNode* node) {    }  } -bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { +bool HLoopOptimization::TryOptimizeInnerLoopFinite(LoopNode* node) {    HBasicBlock* header = node->loop_info->GetHeader();    HBasicBlock* preheader = node->loop_info->GetPreHeader();    // Ensure loop header logic is finite. @@ -813,6 +825,83 @@ bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {    return false;  } +bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { +  return TryOptimizeInnerLoopFinite(node) || +         TryUnrollingForBranchPenaltyReduction(node); +} + +void HLoopOptimization::PeelOrUnrollOnce(LoopNode* loop_node, +                                         bool do_unrolling, +                                         SuperblockCloner::HBasicBlockMap* bb_map, +                                         SuperblockCloner::HInstructionMap* hir_map) { +  // TODO: peel loop nests. +  DCHECK(loop_node->inner == nullptr); + +  // Check that loop info is up-to-date. +  HLoopInformation* loop_info = loop_node->loop_info; +  HBasicBlock* header = loop_info->GetHeader(); +  DCHECK(loop_info == header->GetLoopInformation()); + +  PeelUnrollHelper helper(loop_info, bb_map, hir_map); +  DCHECK(helper.IsLoopClonable()); +  HBasicBlock* new_header = do_unrolling ? helper.DoUnrolling() : helper.DoPeeling(); +  DCHECK(header == new_header); +  DCHECK(loop_info == new_header->GetLoopInformation()); +} + +// +// Loop unrolling: generic part methods. +// + +bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* loop_node) { +  // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests) +  // as InstructionSet is needed. +  if (!kEnableScalarUnrolling || compiler_driver_ == nullptr) { +    return false; +  } + +  HLoopInformation* loop_info = loop_node->loop_info; +  int64_t trip_count = 0; +  // Only unroll loops with a known tripcount. +  if (!induction_range_.HasKnownTripCount(loop_info, &trip_count)) { +    return false; +  } + +  uint32_t unrolling_factor = arch_loop_helper_->GetScalarUnrollingFactor(loop_info, trip_count); +  if (unrolling_factor == kNoUnrollingFactor) { +    return false; +  } + +  LoopAnalysisInfo loop_analysis_info(loop_info); +  LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info); + +  // Check "IsLoopClonable" last as it can be time-consuming. +  if (arch_loop_helper_->IsLoopTooBigForScalarUnrolling(&loop_analysis_info) || +      (loop_analysis_info.GetNumberOfExits() > 1) || +      loop_analysis_info.HasInstructionsPreventingScalarUnrolling() || +      !PeelUnrollHelper::IsLoopClonable(loop_info)) { +    return false; +  } + +  // TODO: support other unrolling factors. +  DCHECK_EQ(unrolling_factor, 2u); + +  // Perform unrolling. +  ArenaAllocator* arena = loop_info->GetHeader()->GetGraph()->GetAllocator(); +  SuperblockCloner::HBasicBlockMap bb_map( +      std::less<HBasicBlock*>(), arena->Adapter(kArenaAllocSuperblockCloner)); +  SuperblockCloner::HInstructionMap hir_map( +      std::less<HInstruction*>(), arena->Adapter(kArenaAllocSuperblockCloner)); +  PeelOrUnrollOnce(loop_node, /* unrolling */ true, &bb_map, &hir_map); + +  // Remove the redundant loop check after unrolling. +  HIf* copy_hif = bb_map.Get(loop_info->GetHeader())->GetLastInstruction()->AsIf(); +  int32_t constant = loop_info->Contains(*copy_hif->IfTrueSuccessor()) ? 1 : 0; +  copy_hif->ReplaceInput(graph_->GetIntConstant(constant), 0u); + +  return true; +} +  //  // Loop vectorization. The implementation is based on the book by Aart J.C. Bik:  // "The Software Vectorization Handbook. Applying Multimedia Extensions for Maximum Performance." @@ -943,7 +1032,8 @@ void HLoopOptimization::Vectorize(LoopNode* node,    HBasicBlock* preheader = node->loop_info->GetPreHeader();    // Pick a loop unrolling factor for the vector loop. -  uint32_t unroll = GetUnrollingFactor(block, trip_count); +  uint32_t unroll = arch_loop_helper_->GetSIMDUnrollingFactor( +      block, trip_count, MaxNumberPeeled(), vector_length_);    uint32_t chunk = vector_length_ * unroll;    DCHECK(trip_count == 0 || (trip_count >= MaxNumberPeeled() + chunk)); @@ -1439,8 +1529,7 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node,        return false;  // reject, unless all operands are same-extension narrower      }      // Accept MIN/MAX(x, y) for vectorizable operands. -    DCHECK(r != nullptr); -    DCHECK(s != nullptr); +    DCHECK(r != nullptr && s != nullptr);      if (generate_code && vector_mode_ != kVector) {  // de-idiom        r = opa;        s = opb; @@ -1944,31 +2033,37 @@ bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node,        instruction->GetType() != DataType::Type::kInt64) {      return false;    } -  // Clipped addition or subtraction? +  // Clipped addition or subtraction on narrower operands? We will try both +  // formats since, e.g., x+c can be interpreted as x+c and x-(-c), depending +  // on what clipping values are used, to get most benefits.    int64_t lo = std::numeric_limits<int64_t>::min();    int64_t hi = std::numeric_limits<int64_t>::max();    HInstruction* clippee = FindClippee(instruction, &lo, &hi); -  bool is_add = true; -  if (clippee->IsAdd()) { -    is_add = true; -  } else if (clippee->IsSub()) { -    is_add = false; -  } else { -    return false;  // clippee is not add/sub -  } -  // Addition or subtraction on narrower operands? +  HInstruction* a = nullptr; +  HInstruction* b = nullptr;    HInstruction* r = nullptr;    HInstruction* s = nullptr;    bool is_unsigned = false; -  if (IsNarrowerOperands(clippee->InputAt(0), clippee->InputAt(1), type, &r, &s, &is_unsigned) && -      (is_add ? IsSaturatedAdd(clippee, type, lo, hi, is_unsigned) -              : IsSaturatedSub(clippee, type, lo, hi, is_unsigned))) { -    DCHECK(r != nullptr); -    DCHECK(s != nullptr); +  bool is_add = true; +  int64_t c = 0; +  // First try for saturated addition. +  if (IsAddConst2(graph_, clippee, /*out*/ &a, /*out*/ &b, /*out*/ &c) && c == 0 && +      IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && +      IsSaturatedAdd(r, s, type, lo, hi, is_unsigned)) { +    is_add = true;    } else { -    return false; +    // Then try again for saturated subtraction. +    a = b = r = s = nullptr; +    if (IsSubConst2(graph_, clippee, /*out*/ &a, /*out*/ &b) && +        IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && +        IsSaturatedSub(r, type, lo, hi, is_unsigned)) { +      is_add = false; +    } else { +      return false; +    }    }    // Accept saturation idiom for vectorizable operands. +  DCHECK(r != nullptr && s != nullptr);    if (generate_code && vector_mode_ != kVector) {  // de-idiom      r = instruction->InputAt(0);      s = instruction->InputAt(1); @@ -2019,8 +2114,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,      HInstruction* a = nullptr;      HInstruction* b = nullptr;      int64_t       c = 0; -    if (IsAddConst(instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { -      DCHECK(a != nullptr && b != nullptr); +    if (IsAddConst2(graph_, instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) {        // Accept c == 1 (rounded) or c == 0 (not rounded).        bool is_rounded = false;        if (c == 1) { @@ -2042,8 +2136,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,        }        // Accept recognized halving add for vectorizable operands. Vectorized code uses the        // shorthand idiomatic operation. Sequential code uses the original scalar expressions. -      DCHECK(r != nullptr); -      DCHECK(s != nullptr); +      DCHECK(r != nullptr && s != nullptr);        if (generate_code && vector_mode_ != kVector) {  // de-idiom          r = instruction->InputAt(0);          s = instruction->InputAt(1); @@ -2093,19 +2186,11 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node,    HInstruction* v = instruction->InputAt(1);    HInstruction* a = nullptr;    HInstruction* b = nullptr; -  if (v->GetType() == reduction_type && v->IsAbs()) { -    HInstruction* x = v->InputAt(0); -    if (x->GetType() == reduction_type) { -      int64_t c = 0; -      if (x->IsSub()) { -        a = x->InputAt(0); -        b = x->InputAt(1); -      } else if (IsAddConst(x, /*out*/ &a, /*out*/ &c)) { -        b = graph_->GetConstant(reduction_type, -c);  // hidden SUB! -      } -    } -  } -  if (a == nullptr || b == nullptr) { +  if (v->IsAbs() && +      v->GetType() == reduction_type && +      IsSubConst2(graph_, v->InputAt(0), /*out*/ &a, /*out*/ &b)) { +    DCHECK(a != nullptr && b != nullptr); +  } else {      return false;    }    // Accept same-type or consistent sign extension for narrower-type on operands a and b. @@ -2138,8 +2223,7 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node,    }    // Accept SAD idiom for vectorizable operands. Vectorized code uses the shorthand    // idiomatic operation. Sequential code uses the original scalar expressions. -  DCHECK(r != nullptr); -  DCHECK(s != nullptr); +  DCHECK(r != nullptr && s != nullptr);    if (generate_code && vector_mode_ != kVector) {  // de-idiom      r = s = v->InputAt(0);    } @@ -2226,41 +2310,6 @@ bool HLoopOptimization::IsVectorizationProfitable(int64_t trip_count) {    return true;  } -static constexpr uint32_t ARM64_SIMD_MAXIMUM_UNROLL_FACTOR = 8; -static constexpr uint32_t ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE = 50; - -uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) { -  uint32_t max_peel = MaxNumberPeeled(); -  switch (compiler_driver_->GetInstructionSet()) { -    case InstructionSet::kArm64: { -      // Don't unroll with insufficient iterations. -      // TODO: Unroll loops with unknown trip count. -      DCHECK_NE(vector_length_, 0u); -      if (trip_count < (2 * vector_length_ + max_peel)) { -        return kNoUnrollingFactor; -      } -      // Don't unroll for large loop body size. -      uint32_t instruction_count = block->GetInstructions().CountSize(); -      if (instruction_count >= ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE) { -        return kNoUnrollingFactor; -      } -      // Find a beneficial unroll factor with the following restrictions: -      //  - At least one iteration of the transformed loop should be executed. -      //  - The loop body shouldn't be "too big" (heuristic). -      uint32_t uf1 = ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE / instruction_count; -      uint32_t uf2 = (trip_count - max_peel) / vector_length_; -      uint32_t unroll_factor = -          TruncToPowerOfTwo(std::min({uf1, uf2, ARM64_SIMD_MAXIMUM_UNROLL_FACTOR})); -      DCHECK_GE(unroll_factor, 1u); -      return unroll_factor; -    } -    case InstructionSet::kX86: -    case InstructionSet::kX86_64: -    default: -      return kNoUnrollingFactor; -  } -} -  //  // Helpers.  // diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 9414e5a0c6..0120cffa56 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -20,12 +20,15 @@  #include "base/scoped_arena_allocator.h"  #include "base/scoped_arena_containers.h"  #include "induction_var_range.h" +#include "loop_analysis.h"  #include "nodes.h"  #include "optimization.h" +#include "superblock_cloner.h"  namespace art {  class CompilerDriver; +class ArchDefaultLoopHelper;  /**   * Loop optimizations. Builds a loop hierarchy and applies optimizations to @@ -135,10 +138,26 @@ class HLoopOptimization : public HOptimization {    void SimplifyInduction(LoopNode* node);    void SimplifyBlocks(LoopNode* node); -  // Performs optimizations specific to inner loop (empty loop removal, +  // Performs optimizations specific to inner loop with finite header logic (empty loop removal,    // unrolling, vectorization). Returns true if anything changed. +  bool TryOptimizeInnerLoopFinite(LoopNode* node); + +  // Performs optimizations specific to inner loop. Returns true if anything changed.    bool OptimizeInnerLoop(LoopNode* node); +  // Performs loop peeling/unrolling once (depends on the 'do_unrolling'); the transformation +  // preserves the header and the loop info. +  // +  // Note: the function records copying information about blocks and instructions. +  void PeelOrUnrollOnce(LoopNode* loop_node, +                        bool do_unrolling, +                        SuperblockCloner::HBasicBlockMap* bb_map, +                        SuperblockCloner::HInstructionMap* hir_map); + +  // Tries to apply loop unrolling for branch penalty reduction and better instruction scheduling +  // opportunities. Returns whether transformation happened. +  bool TryUnrollingForBranchPenaltyReduction(LoopNode* loop_node); +    //    // Vectorization analysis and synthesis.    // @@ -203,7 +222,6 @@ class HLoopOptimization : public HOptimization {                              const ArrayReference* peeling_candidate);    uint32_t MaxNumberPeeled();    bool IsVectorizationProfitable(int64_t trip_count); -  uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count);    //    // Helpers. @@ -297,6 +315,9 @@ class HLoopOptimization : public HOptimization {    HBasicBlock* vector_body_;  // body of the new loop    HInstruction* vector_index_;  // normalized index of the new loop +  // Helper for target-specific behaviour for loop optimizations. +  ArchDefaultLoopHelper* arch_loop_helper_; +    friend class LoopOptimizationTest;    DISALLOW_COPY_AND_ASSIGN(HLoopOptimization); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index d3212cbbc0..f784f8f7f3 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -3103,6 +3103,8 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) {        return os << "array_object_check";      case TypeCheckKind::kArrayCheck:        return os << "array_check"; +    case TypeCheckKind::kBitstringCheck: +      return os << "bitstring_check";      default:        LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast<int>(rhs);        UNREACHABLE(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index a8fcea2097..79d733060b 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6178,8 +6178,7 @@ class HLoadClass FINAL : public HInstruction {          special_input_(HUserRecord<HInstruction*>(current_method)),          type_index_(type_index),          dex_file_(dex_file), -        klass_(klass), -        loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { +        klass_(klass) {      // Referrers class should not need access check. We never inline unverified      // methods so we can't possibly end up in this situation.      DCHECK(!is_referrers_class || !needs_access_check); @@ -6189,6 +6188,7 @@ class HLoadClass FINAL : public HInstruction {      SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);      SetPackedFlag<kFlagIsInBootImage>(false);      SetPackedFlag<kFlagGenerateClInitCheck>(false); +    SetPackedFlag<kFlagValidLoadedClassRTI>(false);    }    bool IsClonable() const OVERRIDE { return true; } @@ -6243,13 +6243,18 @@ class HLoadClass FINAL : public HInstruction {    }    ReferenceTypeInfo GetLoadedClassRTI() { -    return loaded_class_rti_; +    if (GetPackedFlag<kFlagValidLoadedClassRTI>()) { +      // Note: The is_exact flag from the return value should not be used. +      return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); +    } else { +      return ReferenceTypeInfo::CreateInvalid(); +    }    } -  void SetLoadedClassRTI(ReferenceTypeInfo rti) { -    // Make sure we only set exact types (the loaded class should never be merged). -    DCHECK(rti.IsExact()); -    loaded_class_rti_ = rti; +  // Loaded class RTI is marked as valid by RTP if the klass_ is admissible. +  void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { +    DCHECK(klass_ != nullptr); +    SetPackedFlag<kFlagValidLoadedClassRTI>(true);    }    dex::TypeIndex GetTypeIndex() const { return type_index_; } @@ -6302,7 +6307,8 @@ class HLoadClass FINAL : public HInstruction {    static constexpr size_t kFieldLoadKind           = kFlagGenerateClInitCheck + 1;    static constexpr size_t kFieldLoadKindSize =        MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast)); -  static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize; +  static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize; +  static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1;    static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields.");    using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>; @@ -6329,8 +6335,6 @@ class HLoadClass FINAL : public HInstruction {    const DexFile& dex_file_;    Handle<mirror::Class> klass_; - -  ReferenceTypeInfo loaded_class_rti_;  };  std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); @@ -6882,72 +6886,159 @@ enum class TypeCheckKind {    kInterfaceCheck,        // No optimization yet when checking against an interface.    kArrayObjectCheck,      // Can just check if the array is not primitive.    kArrayCheck,            // No optimization yet when checking against a generic array. +  kBitstringCheck,        // Compare the type check bitstring.    kLast = kArrayCheck  };  std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); -class HInstanceOf FINAL : public HExpression<2> { +// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an +// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.) +class HTypeCheckInstruction : public HVariableInputSizeInstruction {   public: -  HInstanceOf(HInstruction* object, -              HLoadClass* target_class, -              TypeCheckKind check_kind, -              uint32_t dex_pc) -      : HExpression(kInstanceOf, -                    DataType::Type::kBool, -                    SideEffectsForArchRuntimeCalls(check_kind), -                    dex_pc) { +  HTypeCheckInstruction(InstructionKind kind, +                        HInstruction* object, +                        HInstruction* target_class_or_null, +                        TypeCheckKind check_kind, +                        Handle<mirror::Class> klass, +                        uint32_t dex_pc, +                        ArenaAllocator* allocator, +                        HIntConstant* bitstring_path_to_root, +                        HIntConstant* bitstring_mask, +                        SideEffects side_effects) +      : HVariableInputSizeInstruction( +          kind, +          side_effects, +          dex_pc, +          allocator, +          /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u, +          kArenaAllocTypeCheckInputs), +        klass_(klass) {      SetPackedField<TypeCheckKindField>(check_kind);      SetPackedFlag<kFlagMustDoNullCheck>(true); +    SetPackedFlag<kFlagValidTargetClassRTI>(false);      SetRawInputAt(0, object); -    SetRawInputAt(1, target_class); +    SetRawInputAt(1, target_class_or_null); +    DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr); +    DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr); +    if (check_kind == TypeCheckKind::kBitstringCheck) { +      DCHECK(target_class_or_null->IsNullConstant()); +      SetRawInputAt(2, bitstring_path_to_root); +      SetRawInputAt(3, bitstring_mask); +    } else { +      DCHECK(target_class_or_null->IsLoadClass()); +    }    }    HLoadClass* GetTargetClass() const { +    DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);      HInstruction* load_class = InputAt(1);      DCHECK(load_class->IsLoadClass());      return load_class->AsLoadClass();    } -  bool IsClonable() const OVERRIDE { return true; } -  bool CanBeMoved() const OVERRIDE { return true; } +  uint32_t GetBitstringPathToRoot() const { +    DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); +    HInstruction* path_to_root = InputAt(2); +    DCHECK(path_to_root->IsIntConstant()); +    return static_cast<uint32_t>(path_to_root->AsIntConstant()->GetValue()); +  } -  bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { -    return true; +  uint32_t GetBitstringMask() const { +    DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); +    HInstruction* mask = InputAt(3); +    DCHECK(mask->IsIntConstant()); +    return static_cast<uint32_t>(mask->AsIntConstant()->GetValue());    } -  bool NeedsEnvironment() const OVERRIDE { -    return CanCallRuntime(GetTypeCheckKind()); +  bool IsClonable() const OVERRIDE { return true; } +  bool CanBeMoved() const OVERRIDE { return true; } + +  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { +    DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName(); +    return GetPackedFields() == down_cast<const HTypeCheckInstruction*>(other)->GetPackedFields();    } -  // Used only in code generation.    bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }    void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }    TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }    bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } -  static bool CanCallRuntime(TypeCheckKind check_kind) { -    // Mips currently does runtime calls for any other checks. -    return check_kind != TypeCheckKind::kExactCheck; +  ReferenceTypeInfo GetTargetClassRTI() { +    if (GetPackedFlag<kFlagValidTargetClassRTI>()) { +      // Note: The is_exact flag from the return value should not be used. +      return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); +    } else { +      return ReferenceTypeInfo::CreateInvalid(); +    }    } -  static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { -    return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); +  // Target class RTI is marked as valid by RTP if the klass_ is admissible. +  void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { +    DCHECK(klass_ != nullptr); +    SetPackedFlag<kFlagValidTargetClassRTI>(true);    } -  DECLARE_INSTRUCTION(InstanceOf); +  Handle<mirror::Class> GetClass() const { +    return klass_; +  }   protected: -  DEFAULT_COPY_CONSTRUCTOR(InstanceOf); +  DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction);   private: -  static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; +  static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;    static constexpr size_t kFieldTypeCheckKindSize =        MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));    static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; -  static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; +  static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1; +  static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1;    static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");    using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>; + +  Handle<mirror::Class> klass_; +}; + +class HInstanceOf FINAL : public HTypeCheckInstruction { + public: +  HInstanceOf(HInstruction* object, +              HInstruction* target_class_or_null, +              TypeCheckKind check_kind, +              Handle<mirror::Class> klass, +              uint32_t dex_pc, +              ArenaAllocator* allocator, +              HIntConstant* bitstring_path_to_root, +              HIntConstant* bitstring_mask) +      : HTypeCheckInstruction(kInstanceOf, +                              object, +                              target_class_or_null, +                              check_kind, +                              klass, +                              dex_pc, +                              allocator, +                              bitstring_path_to_root, +                              bitstring_mask, +                              SideEffectsForArchRuntimeCalls(check_kind)) {} + +  DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; } + +  bool NeedsEnvironment() const OVERRIDE { +    return CanCallRuntime(GetTypeCheckKind()); +  } + +  static bool CanCallRuntime(TypeCheckKind check_kind) { +    // Mips currently does runtime calls for any other checks. +    return check_kind != TypeCheckKind::kExactCheck; +  } + +  static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { +    return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); +  } + +  DECLARE_INSTRUCTION(InstanceOf); + + protected: +  DEFAULT_COPY_CONSTRUCTOR(InstanceOf);  };  class HBoundType FINAL : public HExpression<1> { @@ -6997,31 +7088,26 @@ class HBoundType FINAL : public HExpression<1> {    ReferenceTypeInfo upper_bound_;  }; -class HCheckCast FINAL : public HTemplateInstruction<2> { +class HCheckCast FINAL : public HTypeCheckInstruction {   public:    HCheckCast(HInstruction* object, -             HLoadClass* target_class, +             HInstruction* target_class_or_null,               TypeCheckKind check_kind, -             uint32_t dex_pc) -      : HTemplateInstruction(kCheckCast, SideEffects::CanTriggerGC(), dex_pc) { -    SetPackedField<TypeCheckKindField>(check_kind); -    SetPackedFlag<kFlagMustDoNullCheck>(true); -    SetRawInputAt(0, object); -    SetRawInputAt(1, target_class); -  } - -  HLoadClass* GetTargetClass() const { -    HInstruction* load_class = InputAt(1); -    DCHECK(load_class->IsLoadClass()); -    return load_class->AsLoadClass(); -  } - -  bool IsClonable() const OVERRIDE { return true; } -  bool CanBeMoved() const OVERRIDE { return true; } - -  bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { -    return true; -  } +             Handle<mirror::Class> klass, +             uint32_t dex_pc, +             ArenaAllocator* allocator, +             HIntConstant* bitstring_path_to_root, +             HIntConstant* bitstring_mask) +      : HTypeCheckInstruction(kCheckCast, +                              object, +                              target_class_or_null, +                              check_kind, +                              klass, +                              dex_pc, +                              allocator, +                              bitstring_path_to_root, +                              bitstring_mask, +                              SideEffects::CanTriggerGC()) {}    bool NeedsEnvironment() const OVERRIDE {      // Instruction may throw a CheckCastError. @@ -7030,24 +7116,10 @@ class HCheckCast FINAL : public HTemplateInstruction<2> {    bool CanThrow() const OVERRIDE { return true; } -  bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); } -  void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); } -  TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); } -  bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } -    DECLARE_INSTRUCTION(CheckCast);   protected:    DEFAULT_COPY_CONSTRUCTOR(CheckCast); - - private: -  static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; -  static constexpr size_t kFieldTypeCheckKindSize = -      MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast)); -  static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; -  static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; -  static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); -  using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;  };  /** diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 00194ff1fe..e0a9cfb934 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -99,6 +99,7 @@ enum class MethodCompilationStat {    kConstructorFenceRemovedLSE,    kConstructorFenceRemovedPFRA,    kConstructorFenceRemovedCFRE, +  kBitstringTypeCheck,    kJitOutOfMemoryForCommit,    kLastStat  }; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index f843c008d8..59733397bf 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -34,6 +34,20 @@ void PrepareForRegisterAllocation::Run() {    }  } +void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) { +  // Record only those bitstring type checks that make it to the codegen stage. +  if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { +    MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); +  } +} + +void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) { +  // Record only those bitstring type checks that make it to the codegen stage. +  if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { +    MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); +  } +} +  void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) {    check->ReplaceWith(check->InputAt(0));  } diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index 2c64f016c1..f6e4d3ef99 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -40,6 +40,8 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor {        "prepare_for_register_allocation";   private: +  void VisitCheckCast(HCheckCast* check_cast) OVERRIDE; +  void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE;    void VisitNullCheck(HNullCheck* check) OVERRIDE;    void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE;    void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 67a61fc01d..4030883a57 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -87,6 +87,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {    void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE;    void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;    void VisitLoadClass(HLoadClass* load_class) OVERRIDE; +  void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE;    void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;    void VisitLoadString(HLoadString* instr) OVERRIDE;    void VisitLoadException(HLoadException* instr) OVERRIDE; @@ -171,6 +172,12 @@ void ReferenceTypePropagation::ValidateTypes() {                  << "NullCheck " << instr->GetReferenceTypeInfo()                  << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo();            } +        } else if (instr->IsInstanceOf()) { +          HInstanceOf* iof = instr->AsInstanceOf(); +          DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact()); +        } else if (instr->IsCheckCast()) { +          HCheckCast* check = instr->AsCheckCast(); +          DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact());          }        }      } @@ -499,8 +506,7 @@ void ReferenceTypePropagation::RTPVisitor::BoundTypeForIfInstanceOf(HBasicBlock*      return;    } -  HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); -  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); +  ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI();    if (!class_rti.IsValid()) {      // He have loaded an unresolved class. Don't bother bounding the type.      return; @@ -643,15 +649,20 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet(  void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) {    ScopedObjectAccess soa(Thread::Current()); -  Handle<mirror::Class> resolved_class = instr->GetClass(); -  if (IsAdmissible(resolved_class.Get())) { -    instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( -        resolved_class, /* is_exact */ true)); +  if (IsAdmissible(instr->GetClass().Get())) { +    instr->SetValidLoadedClassRTI();    }    instr->SetReferenceTypeInfo(        ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true));  } +void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) { +  ScopedObjectAccess soa(Thread::Current()); +  if (IsAdmissible(instr->GetClass().Get())) { +    instr->SetValidTargetClassRTI(); +  } +} +  void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) {    instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());  } @@ -719,8 +730,6 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) {  }  void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { -  HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); -  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();    HBoundType* bound_type = check_cast->GetNext()->AsBoundType();    if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) {      // The next instruction is not an uninitialized BoundType. This must be @@ -729,12 +738,14 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast    }    DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0)); -  if (class_rti.IsValid()) { +  ScopedObjectAccess soa(Thread::Current()); +  Handle<mirror::Class> klass = check_cast->GetClass(); +  if (IsAdmissible(klass.Get())) {      DCHECK(is_first_run_); -    ScopedObjectAccess soa(Thread::Current()); +    check_cast->SetValidTargetClassRTI();      // This is the first run of RTP and class is resolved. -    bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); -    bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact), +    bool is_exact = klass->CannotBeAssignedFromOtherTypes(); +    bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact),                                /* CheckCast succeeds for nulls. */ true);    } else {      // This is the first run of RTP and class is unresolved. Remove the binding. diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 7dffb2a378..70b45763af 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -240,6 +240,75 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(    return load_kind;  } +static inline bool CanUseTypeCheckBitstring(ObjPtr<mirror::Class> klass, +                                            CodeGenerator* codegen, +                                            CompilerDriver* compiler_driver) +    REQUIRES_SHARED(Locks::mutator_lock_) { +  DCHECK(!klass->IsProxyClass()); +  DCHECK(!klass->IsArrayClass()); + +  if (Runtime::Current()->UseJitCompilation()) { +    // If we're JITting, try to assign a type check bitstring (fall through). +  } else if (codegen->GetCompilerOptions().IsBootImage()) { +    const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex()); +    if (!compiler_driver->IsImageClass(descriptor)) { +      return false; +    } +    // If the target is a boot image class, try to assign a type check bitstring (fall through). +    // (If --force-determinism, this was already done; repeating is OK and yields the same result.) +  } else { +    // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring +    // already assigned in the boot image. +    return false; +  } + +  // Try to assign a type check bitstring. +  MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); +  if ((false) &&  // FIXME: Inliner does not respect compiler_driver->IsClassToCompile() +                  // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569 +      kIsDebugBuild && +      codegen->GetCompilerOptions().IsBootImage() && +      codegen->GetCompilerOptions().IsForceDeterminism()) { +    SubtypeCheckInfo::State old_state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass); +    CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed) +        << klass->PrettyDescriptor() << "/" << old_state +        << " in " << codegen->GetGraph()->PrettyMethod(); +  } +  SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass); +  return state == SubtypeCheckInfo::kAssigned; +} + +TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr<mirror::Class> klass, +                                                CodeGenerator* codegen, +                                                CompilerDriver* compiler_driver, +                                                bool needs_access_check) { +  if (klass == nullptr) { +    return TypeCheckKind::kUnresolvedCheck; +  } else if (klass->IsInterface()) { +    return TypeCheckKind::kInterfaceCheck; +  } else if (klass->IsArrayClass()) { +    if (klass->GetComponentType()->IsObjectClass()) { +      return TypeCheckKind::kArrayObjectCheck; +    } else if (klass->CannotBeAssignedFromOtherTypes()) { +      return TypeCheckKind::kExactCheck; +    } else { +      return TypeCheckKind::kArrayCheck; +    } +  } else if (klass->IsFinal()) {  // TODO: Consider using bitstring for final classes. +    return TypeCheckKind::kExactCheck; +  } else if (kBitstringSubtypeCheckEnabled && +             !needs_access_check && +             CanUseTypeCheckBitstring(klass, codegen, compiler_driver)) { +    // TODO: We should not need the `!needs_access_check` check but getting rid of that +    // requires rewriting some optimizations in instruction simplifier. +    return TypeCheckKind::kBitstringCheck; +  } else if (klass->IsAbstract()) { +    return TypeCheckKind::kAbstractClassCheck; +  } else { +    return TypeCheckKind::kClassHierarchyCheck; +  } +} +  void HSharpening::ProcessLoadString(      HLoadString* load_string,      CodeGenerator* codegen, diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index 6df7d6d91e..fa3e948eeb 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -44,12 +44,10 @@ class HSharpening : public HOptimization {    static constexpr const char* kSharpeningPassName = "sharpening"; -  // Used by the builder. -  static void ProcessLoadString(HLoadString* load_string, -                                CodeGenerator* codegen, -                                CompilerDriver* compiler_driver, -                                const DexCompilationUnit& dex_compilation_unit, -                                VariableSizedHandleScope* handles); +  // Used by Sharpening and InstructionSimplifier. +  static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, +                                          CodeGenerator* codegen, +                                          CompilerDriver* compiler_driver);    // Used by the builder and the inliner.    static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, @@ -58,10 +56,19 @@ class HSharpening : public HOptimization {                                                     const DexCompilationUnit& dex_compilation_unit)        REQUIRES_SHARED(Locks::mutator_lock_); -  // Used by Sharpening and InstructionSimplifier. -  static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, -                                          CodeGenerator* codegen, -                                          CompilerDriver* compiler_driver); +  // Used by the builder. +  static TypeCheckKind ComputeTypeCheckKind(ObjPtr<mirror::Class> klass, +                                            CodeGenerator* codegen, +                                            CompilerDriver* compiler_driver, +                                            bool needs_access_check) +      REQUIRES_SHARED(Locks::mutator_lock_); + +  // Used by the builder. +  static void ProcessLoadString(HLoadString* load_string, +                                CodeGenerator* codegen, +                                CompilerDriver* compiler_driver, +                                const DexCompilationUnit& dex_compilation_unit, +                                VariableSizedHandleScope* handles);   private:    CodeGenerator* codegen_; diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc index 04942f9a4a..ee74f1001f 100644 --- a/compiler/optimizing/superblock_cloner.cc +++ b/compiler/optimizing/superblock_cloner.cc @@ -853,7 +853,7 @@ void SuperblockCloner::CleanUp() {      }    } -  if (kSuperblockClonerVerify) { +  if (kIsDebugBuild) {      VerifyGraph();    }  } diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h index 19c9dd471c..afd5a5d6e7 100644 --- a/compiler/optimizing/superblock_cloner.h +++ b/compiler/optimizing/superblock_cloner.h @@ -25,7 +25,6 @@  namespace art {  static const bool kSuperblockClonerLogging = false; -static const bool kSuperblockClonerVerify = false;  // Represents an edge between two HBasicBlocks.  // diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h index 4bd323dadb..9915498acc 100644 --- a/compiler/utils/atomic_dex_ref_map-inl.h +++ b/compiler/utils/atomic_dex_ref_map-inl.h @@ -81,8 +81,7 @@ inline bool AtomicDexRefMap<DexFileReferenceType, Value>::Remove(const DexFileRe    if (array == nullptr) {      return false;    } -  *out = (*array)[ref.index].load(std::memory_order_relaxed); -  (*array)[ref.index].store(nullptr, std::memory_order_seq_cst); +  *out = (*array)[ref.index].exchange(nullptr, std::memory_order_seq_cst);    return true;  } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 4613b40457..1f471106df 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -121,7 +121,8 @@ DexFile::DexFile(const uint8_t* base,        num_call_site_ids_(0),        oat_dex_file_(oat_dex_file),        container_(std::move(container)), -      is_compact_dex_(is_compact_dex) { +      is_compact_dex_(is_compact_dex), +      is_platform_dex_(false) {    CHECK(begin_ != nullptr) << GetLocation();    CHECK_GT(size_, 0U) << GetLocation();    // Check base (=header) alignment. diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index aeb49d2c25..683a8243ed 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -992,6 +992,14 @@ class DexFile {    ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const;    ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; +  ALWAYS_INLINE bool IsPlatformDexFile() const { +    return is_platform_dex_; +  } + +  ALWAYS_INLINE void SetIsPlatformDexFile() { +    is_platform_dex_ = true; +  } +    bool IsInMainSection(const void* addr) const {      return Begin() <= addr && addr < Begin() + Size();    } @@ -1094,6 +1102,9 @@ class DexFile {    // If the dex file is a compact dex file. If false then the dex file is a standard dex file.    const bool is_compact_dex_; +  // If the dex file is located in /system/framework/. +  bool is_platform_dex_; +    friend class DexFileLoader;    friend class DexFileVerifierTest;    friend class OatWriter; diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h index 441b3c14b5..b62d044c6a 100644 --- a/libdexfile/dex/hidden_api_access_flags.h +++ b/libdexfile/dex/hidden_api_access_flags.h @@ -18,6 +18,7 @@  #define ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_  #include "base/bit_utils.h" +#include "base/macros.h"  #include "dex/modifiers.h"  namespace art { diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index ee83c4a4e8..fcbafe7e71 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -31,7 +31,6 @@  #include "base/leb128.h"  #include "fixed_up_dex_file.h" -#include "dex/art_dex_file_loader.h"  #include "dex/dex_file-inl.h"  #include "dex/dex_file_loader.h"  #include "dex/dex_file_verifier.h" @@ -108,7 +107,12 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi    std::vector<unsigned char> data;    std::unique_ptr<const art::DexFile> new_dex_file;    std::string error; -  const art::ArtDexFileLoader dex_file_loader; + +  // Do not use ArtDexFileLoader here. This code runs in a signal handler and +  // its stack is too small to invoke the required LocationIsOnSystemFramework +  // (b/76429651). Instead, we use DexFileLoader and copy the IsPlatformDexFile +  // property from `original` to `new_dex_file`. +  const art::DexFileLoader dex_file_loader;    if (original.IsCompactDexFile()) {      // Since we are supposed to return a standard dex, convert back using dexlayout. It's OK to do @@ -159,6 +163,10 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi      return nullptr;    } +  if (original.IsPlatformDexFile()) { +    const_cast<art::DexFile*>(new_dex_file.get())->SetIsPlatformDexFile(); +  } +    DoDexUnquicken(*new_dex_file, original);    RecomputeDexChecksum(const_cast<art::DexFile*>(new_dex_file.get())); diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 41a649b5e3..4526be4cbe 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -925,7 +925,9 @@ static jvmtiError GetOwnedMonitorInfoCommon(const art::ScopedObjectAccessAlready      if (target != self) {        called_method = true;        // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. -      if (!target->RequestSynchronousCheckpoint(&closure)) { +      // Since this deals with object references we need to avoid going to sleep. +      art::ScopedAssertNoThreadSuspension sants("Getting owned monitor usage"); +      if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) {          return ERR(THREAD_NOT_ALIVE);        }      } else { diff --git a/runtime/Android.bp b/runtime/Android.bp index 590a399738..c0f1c366b8 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -87,6 +87,7 @@ cc_defaults {          "gc/space/zygote_space.cc",          "gc/task_processor.cc",          "gc/verification.cc", +        "hidden_api.cc",          "hprof/hprof.cc",          "image.cc",          "index_bss_mapping.cc", @@ -476,6 +477,13 @@ art_cc_library {      export_shared_lib_headers: [          "libdexfile",      ], +    target: { +        android: { +            lto: { +                 thin: true, +            }, +        }, +    },  }  art_cc_library { @@ -565,6 +573,7 @@ art_cc_test {          "gc/task_processor_test.cc",          "gtest_test.cc",          "handle_scope_test.cc", +        "hidden_api_test.cc",          "imtable_test.cc",          "indenter_test.cc",          "indirect_reference_table_test.cc", diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 98214fb684..0fd239a244 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -809,6 +809,9 @@ END art_quick_unlock_object_no_inline      .extern artInstanceOfFromCode      .extern artThrowClassCastExceptionForObject  ENTRY art_quick_check_instance_of +    // Type check using the bit string passes null as the target class. In that case just throw. +    cbz r1, .Lthrow_class_cast_exception_for_bitstring_check +      push {r0-r2, lr}                    @ save arguments, padding (r2) and link register      .cfi_adjust_cfa_offset 16      .cfi_rel_offset r0, 0 @@ -827,6 +830,7 @@ ENTRY art_quick_check_instance_of      .cfi_restore r2      .cfi_restore lr +.Lthrow_class_cast_exception_for_bitstring_check:      SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2       @ save all registers as basis for long jump context      mov r2, r9                      @ pass Thread::Current      bl  artThrowClassCastExceptionForObject  @ (Object*, Class*, Thread*) diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index fb449ed5c7..9ff5ebede3 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1269,6 +1269,9 @@ END art_quick_unlock_object_no_inline      .extern artInstanceOfFromCode      .extern artThrowClassCastExceptionForObject  ENTRY art_quick_check_instance_of +    // Type check using the bit string passes null as the target class. In that case just throw. +    cbz x1, .Lthrow_class_cast_exception_for_bitstring_check +      // Store arguments and link register      // Stack needs to be 16B aligned on calls.      SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32 @@ -1294,6 +1297,7 @@ ENTRY art_quick_check_instance_of      // Restore      RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32 +.Lthrow_class_cast_exception_for_bitstring_check:      SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context      mov x2, xSELF                     // pass Thread::Current      bl artThrowClassCastExceptionForObject     // (Object*, Class*, Thread*) diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index b2f7e10f52..d8fe480719 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1423,6 +1423,10 @@ END art_quick_unlock_object_no_inline      .extern artInstanceOfFromCode      .extern artThrowClassCastExceptionForObject  ENTRY art_quick_check_instance_of +    // Type check using the bit string passes null as the target class. In that case just throw. +    beqz   $a1, .Lthrow_class_cast_exception_for_bitstring_check +    nop +      addiu  $sp, $sp, -32      .cfi_adjust_cfa_offset 32      sw     $gp, 16($sp) @@ -1441,12 +1445,15 @@ ENTRY art_quick_check_instance_of      jalr   $zero, $ra      addiu  $sp, $sp, 32      .cfi_adjust_cfa_offset -32 +  .Lthrow_class_cast_exception:      lw     $t9, 8($sp)      lw     $a1, 4($sp)      lw     $a0, 0($sp)      addiu  $sp, $sp, 32      .cfi_adjust_cfa_offset -32 + +.Lthrow_class_cast_exception_for_bitstring_check:      SETUP_SAVE_ALL_CALLEE_SAVES_FRAME      la   $t9, artThrowClassCastExceptionForObject      jalr $zero, $t9                 # artThrowClassCastException (Object*, Class*, Thread*) diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 58e0e44813..8d2a7bd6c1 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1364,6 +1364,9 @@ END art_quick_unlock_object_no_inline      .extern artInstanceOfFromCode      .extern artThrowClassCastExceptionForObject  ENTRY art_quick_check_instance_of +    // Type check using the bit string passes null as the target class. In that case just throw. +    beqzc  $a1, .Lthrow_class_cast_exception_for_bitstring_check +      daddiu $sp, $sp, -32      .cfi_adjust_cfa_offset 32      sd     $ra, 24($sp) @@ -1379,12 +1382,15 @@ ENTRY art_quick_check_instance_of      jalr   $zero, $ra      daddiu $sp, $sp, 32      .cfi_adjust_cfa_offset -32 +  .Lthrow_class_cast_exception:      ld     $t9, 16($sp)      ld     $a1, 8($sp)      ld     $a0, 0($sp)      daddiu $sp, $sp, 32      .cfi_adjust_cfa_offset -32 + +.Lthrow_class_cast_exception_for_bitstring_check:      SETUP_GP      SETUP_SAVE_ALL_CALLEE_SAVES_FRAME      dla  $t9, artThrowClassCastExceptionForObject diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 5c4ae4ea12..df43aef94b 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1432,6 +1432,10 @@ DEFINE_FUNCTION art_quick_instance_of  END_FUNCTION art_quick_instance_of  DEFINE_FUNCTION art_quick_check_instance_of +    // Type check using the bit string passes null as the target class. In that case just throw. +    testl %ecx, %ecx +    jz .Lthrow_class_cast_exception_for_bitstring_check +      PUSH eax                              // alignment padding      PUSH ecx                              // pass arg2 - checked class      PUSH eax                              // pass arg1 - obj @@ -1449,6 +1453,7 @@ DEFINE_FUNCTION art_quick_check_instance_of      addl LITERAL(4), %esp      CFI_ADJUST_CFA_OFFSET(-4) +.Lthrow_class_cast_exception_for_bitstring_check:      SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context      // Outgoing argument set up      PUSH eax                              // alignment padding diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index a813200606..4f941e1c48 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1403,6 +1403,10 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline  END_FUNCTION art_quick_unlock_object_no_inline  DEFINE_FUNCTION art_quick_check_instance_of +    // Type check using the bit string passes null as the target class. In that case just throw. +    testl %esi, %esi +    jz .Lthrow_class_cast_exception_for_bitstring_check +      // We could check the super classes here but that is usually already checked in the caller.      PUSH rdi                          // Save args for exc      PUSH rsi @@ -1426,6 +1430,7 @@ DEFINE_FUNCTION art_quick_check_instance_of      POP rsi                           // Pop arguments      POP rdi +.Lthrow_class_cast_exception_for_bitstring_check:      SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context      mov %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()      call SYMBOL(artThrowClassCastExceptionForObject)  // (Object* src, Class* dest, Thread*) diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 292bde0272..fe0f876d66 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -56,6 +56,7 @@ const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {    "CtorFenceIns ",    "InvokeInputs ",    "PhiInputs    ", +  "TypeCheckIns ",    "LoopInfo     ",    "LIBackEdges  ",    "TryCatchInf  ", diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index c3011091e8..688f01b71f 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -62,6 +62,7 @@ enum ArenaAllocKind {    kArenaAllocConstructorFenceInputs,    kArenaAllocInvokeInputs,    kArenaAllocPhiInputs, +  kArenaAllocTypeCheckInputs,    kArenaAllocLoopInfo,    kArenaAllocLoopInfoBackEdges,    kArenaAllocTryCatchInfo, diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 1cb3b9c380..2b3e360650 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -319,4 +319,21 @@ bool LocationIsOnSystem(const char* location) {    return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str());  } +bool LocationIsOnSystemFramework(const char* location) { +  std::string error_msg; +  std::string root_path = GetAndroidRootSafe(&error_msg); +  if (root_path.empty()) { +    // Could not find Android root. +    // TODO(dbrazdil): change to stricter GetAndroidRoot() once b/76452688 is resolved. +    return false; +  } +  std::string framework_path = root_path + "/framework/"; + +  // Warning: Bionic implementation of realpath() allocates > 12KB on the stack. +  // Do not run this code on a small stack, e.g. in signal handler. +  UniqueCPtr<const char[]> path(realpath(location, nullptr)); +  return path != nullptr && +         android::base::StartsWith(path.get(), framework_path.c_str()); +} +  }  // namespace art diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h index 7f691d546b..8adb4f7bf8 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -75,6 +75,9 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string&  // Return whether the location is on system (i.e. android root).  bool LocationIsOnSystem(const char* location); +// Return whether the location is on system/framework (i.e. android_root/framework). +bool LocationIsOnSystemFramework(const char* location); +  }  // namespace art  #endif  // ART_RUNTIME_BASE_FILE_UTILS_H_ diff --git a/runtime/base/quasi_atomic.h b/runtime/base/quasi_atomic.h index 067d01db01..0012f6482b 100644 --- a/runtime/base/quasi_atomic.h +++ b/runtime/base/quasi_atomic.h @@ -152,14 +152,6 @@ class QuasiAtomic {      return NeedSwapMutexes(isa);    } -  static void ThreadFenceAcquire() { -    std::atomic_thread_fence(std::memory_order_acquire); -  } - -  static void ThreadFenceRelease() { -    std::atomic_thread_fence(std::memory_order_release); -  } -    static void ThreadFenceForConstructor() {      #if defined(__aarch64__)        __asm__ __volatile__("dmb ishst" : : : "memory"); @@ -168,10 +160,6 @@ class QuasiAtomic {      #endif    } -  static void ThreadFenceSequentiallyConsistent() { -    std::atomic_thread_fence(std::memory_order_seq_cst); -  } -   private:    static Mutex* GetSwapMutex(const volatile int64_t* addr);    static int64_t SwapMutexRead64(volatile const int64_t* addr); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 8b64b8def0..3025818ab7 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -72,6 +72,7 @@  #include "gc/space/space-inl.h"  #include "gc_root-inl.h"  #include "handle_scope-inl.h" +#include "hidden_api.h"  #include "image-inl.h"  #include "imt_conflict_table.h"  #include "imtable-inl.h" @@ -6178,7 +6179,7 @@ ArtMethod* ClassLinker::AddMethodToConflictTable(ObjPtr<mirror::Class> klass,    // Note that there is a race in the presence of multiple threads and we may leak    // memory from the LinearAlloc, but that's a tradeoff compared to using    // atomic operations. -  QuasiAtomic::ThreadFenceRelease(); +  std::atomic_thread_fence(std::memory_order_release);    new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_);    return new_conflict_method;  } @@ -7935,7 +7936,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,    }    DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);    if (resolved != nullptr && -      hiddenapi::ShouldBlockAccessToMember(resolved, class_loader, hiddenapi::kLinking)) { +      hiddenapi::ShouldBlockAccessToMember( +          resolved, class_loader, dex_cache, hiddenapi::kLinking)) {      resolved = nullptr;    }    if (resolved != nullptr) { @@ -8077,7 +8079,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx,      resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);    }    if (resolved != nullptr && -      hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { +      hiddenapi::ShouldBlockAccessToMember( +          resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {      resolved = nullptr;    }    return resolved; @@ -8156,7 +8159,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx,    }    if (resolved == nullptr || -      hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { +      hiddenapi::ShouldBlockAccessToMember( +          resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {      const char* name = dex_file.GetFieldName(field_id);      const char* type = dex_file.GetFieldTypeDescriptor(field_id);      ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); @@ -8189,7 +8193,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx,    StringPiece type(dex_file.GetFieldTypeDescriptor(field_id));    resolved = mirror::Class::FindField(self, klass, name, type);    if (resolved != nullptr && -      hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { +      hiddenapi::ShouldBlockAccessToMember( +          resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {      resolved = nullptr;    }    if (resolved != nullptr) { diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 99a4c77979..4a9449640b 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -4354,9 +4354,11 @@ bool Dbg::DdmHandleChunk(JNIEnv* env,            WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch,            type, dataArray.get(), 0, data.size()));    if (env->ExceptionCheck()) { -    LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type); -    env->ExceptionDescribe(); -    env->ExceptionClear(); +    Thread* self = Thread::Current(); +    ScopedObjectAccess soa(self); +    LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type) << std::endl +              << self->GetException()->Dump(); +    self->ClearException();      return false;    } @@ -4400,10 +4402,11 @@ bool Dbg::DdmHandleChunk(JNIEnv* env,                            reinterpret_cast<jbyte*>(out_data->data()));    if (env->ExceptionCheck()) { +    Thread* self = Thread::Current(); +    ScopedObjectAccess soa(self);      LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x", -                              type); -    env->ExceptionDescribe(); -    env->ExceptionClear(); +                              type) << std::endl << self->GetException()->Dump(); +    self->ClearException();      return false;    } diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index 9802c6904b..f3e6a69279 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -22,6 +22,7 @@  #include "android-base/stringprintf.h"  #include "base/file_magic.h" +#include "base/file_utils.h"  #include "base/stl_util.h"  #include "base/systrace.h"  #include "base/unix_file/fd_file.h" @@ -505,4 +506,39 @@ bool ArtDexFileLoader::OpenAllDexFilesFromZip(    }  } +std::unique_ptr<DexFile> ArtDexFileLoader::OpenCommon(const uint8_t* base, +                                                      size_t size, +                                                      const uint8_t* data_base, +                                                      size_t data_size, +                                                      const std::string& location, +                                                      uint32_t location_checksum, +                                                      const OatDexFile* oat_dex_file, +                                                      bool verify, +                                                      bool verify_checksum, +                                                      std::string* error_msg, +                                                      std::unique_ptr<DexFileContainer> container, +                                                      VerifyResult* verify_result) { +  std::unique_ptr<DexFile> dex_file = DexFileLoader::OpenCommon(base, +                                                                size, +                                                                data_base, +                                                                data_size, +                                                                location, +                                                                location_checksum, +                                                                oat_dex_file, +                                                                verify, +                                                                verify_checksum, +                                                                error_msg, +                                                                std::move(container), +                                                                verify_result); + +  // Check if this dex file is located in the framework directory. +  // If it is, set a flag on the dex file. This is used by hidden API +  // policy decision logic. +  if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) { +    dex_file->SetIsPlatformDexFile(); +  } + +  return dex_file; +} +  }  // namespace art diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h index 7c7a59b37f..7577945632 100644 --- a/runtime/dex/art_dex_file_loader.h +++ b/runtime/dex/art_dex_file_loader.h @@ -120,6 +120,19 @@ class ArtDexFileLoader : public DexFileLoader {                                                         bool verify_checksum,                                                         std::string* error_msg,                                                         ZipOpenErrorCode* error_code) const; + +  static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base, +                                             size_t size, +                                             const uint8_t* data_base, +                                             size_t data_size, +                                             const std::string& location, +                                             uint32_t location_checksum, +                                             const OatDexFile* oat_dex_file, +                                             bool verify, +                                             bool verify_checksum, +                                             std::string* error_msg, +                                             std::unique_ptr<DexFileContainer> container, +                                             VerifyResult* verify_result);  };  }  // namespace art diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index 6e2cfec381..3e0d6662c7 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -16,9 +16,11 @@  #include <sys/mman.h> +#include <fstream>  #include <memory>  #include "art_dex_file_loader.h" +#include "base/file_utils.h"  #include "base/os.h"  #include "base/stl_util.h"  #include "base/unix_file/fd_file.h" @@ -35,7 +37,40 @@  namespace art { -class ArtDexFileLoaderTest : public CommonRuntimeTest {}; +static void Copy(const std::string& src, const std::string& dst) { +  std::ifstream  src_stream(src, std::ios::binary); +  std::ofstream  dst_stream(dst, std::ios::binary); +  dst_stream << src_stream.rdbuf(); +} + +class ArtDexFileLoaderTest : public CommonRuntimeTest { + public: +  virtual void SetUp() { +    CommonRuntimeTest::SetUp(); + +    std::string dex_location = GetTestDexFileName("Main"); + +    data_location_path_ = android_data_ + "/foo.jar"; +    system_location_path_ = GetAndroidRoot() + "/foo.jar"; +    system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; + +    Copy(dex_location, data_location_path_); +    Copy(dex_location, system_location_path_); +    Copy(dex_location, system_framework_location_path_); +  } + +  virtual void TearDown() { +    remove(data_location_path_.c_str()); +    remove(system_location_path_.c_str()); +    remove(system_framework_location_path_.c_str()); +    CommonRuntimeTest::TearDown(); +  } + + protected: +  std::string data_location_path_; +  std::string system_location_path_; +  std::string system_framework_location_path_; +};  // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and  // the tests that depend upon them should be moved to dex_file_loader_test.cc @@ -304,4 +339,57 @@ TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) {    ASSERT_EQ(0, unlink(dex_location_sym.c_str()));  } +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { +  ArtDexFileLoader loader; +  bool success; +  std::string error_msg; +  std::vector<std::unique_ptr<const DexFile>> dex_files; + +  // Load file from a non-system directory and check that it is not flagged as framework. +  ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str())); +  success = loader.Open(data_location_path_.c_str(), +                        data_location_path_, +                        /* verify */ false, +                        /* verify_checksum */ false, +                        &error_msg, +                        &dex_files); +  ASSERT_TRUE(success); +  ASSERT_GE(dex_files.size(), 1u); +  for (std::unique_ptr<const DexFile>& dex_file : dex_files) { +    ASSERT_FALSE(dex_file->IsPlatformDexFile()); +  } + +  dex_files.clear(); + +  // Load file from a system, non-framework directory and check that it is not flagged as framework. +  ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str())); +  success = loader.Open(system_location_path_.c_str(), +                        system_location_path_, +                        /* verify */ false, +                        /* verify_checksum */ false, +                        &error_msg, +                        &dex_files); +  ASSERT_TRUE(success); +  ASSERT_GE(dex_files.size(), 1u); +  for (std::unique_ptr<const DexFile>& dex_file : dex_files) { +    ASSERT_FALSE(dex_file->IsPlatformDexFile()); +  } + +  dex_files.clear(); + +  // Load file from a system/framework directory and check that it is flagged as a framework dex. +  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str())); +  success = loader.Open(system_framework_location_path_.c_str(), +                        system_framework_location_path_, +                        /* verify */ false, +                        /* verify_checksum */ false, +                        &error_msg, +                        &dex_files); +  ASSERT_TRUE(success); +  ASSERT_GE(dex_files.size(), 1u); +  for (std::unique_ptr<const DexFile>& dex_file : dex_files) { +    ASSERT_TRUE(dex_file->IsPlatformDexFile()); +  } +} +  }  // namespace art diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 9b0756b529..ba7fb6b9db 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -16,8 +16,11 @@  #include "art_method-inl.h"  #include "callee_save_frame.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h"  #include "common_throws.h"  #include "mirror/object-inl.h" +#include "nth_caller_visitor.h"  #include "thread.h"  #include "well_known_classes.h" @@ -112,6 +115,26 @@ extern "C" NO_RETURN void artThrowClassCastException(mirror::Class* dest_type,                                                       Thread* self)      REQUIRES_SHARED(Locks::mutator_lock_) {    ScopedQuickEntrypointChecks sqec(self); +  if (dest_type == nullptr) { +    // Find the target class for check cast using the bitstring check (dest_type == null). +    NthCallerVisitor visitor(self, 0u); +    visitor.WalkStack(); +    DCHECK(visitor.caller != nullptr); +    uint32_t dex_pc = visitor.GetDexPc(); +    CodeItemDataAccessor accessor(*visitor.caller->GetDexFile(), visitor.caller->GetCodeItem()); +    const Instruction& check_cast = accessor.InstructionAt(dex_pc); +    DCHECK_EQ(check_cast.Opcode(), Instruction::CHECK_CAST); +    dex::TypeIndex type_index(check_cast.VRegB_21c()); +    ClassLinker* linker = Runtime::Current()->GetClassLinker(); +    dest_type = linker->LookupResolvedType(type_index, visitor.caller).Ptr(); +    CHECK(dest_type != nullptr) << "Target class should have been previously resolved: " +        << visitor.caller->GetDexFile()->PrettyType(type_index); +    CHECK(!dest_type->IsAssignableFrom(src_type)) +        << " " << std::hex << dest_type->PrettyDescriptor() << ";" << dest_type->Depth() +        << "/" << dest_type->GetField32(mirror::Class::StatusOffset()) +        << " <: " << src_type->PrettyDescriptor() << ";" << src_type->Depth() +        << "/" << src_type->GetField32(mirror::Class::StatusOffset()); +  }    DCHECK(!dest_type->IsAssignableFrom(src_type));    ThrowClassCastException(dest_type, src_type);    self->QuickDeliverException(); diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index bb5167f15d..0747c3c77b 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2357,14 +2357,13 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,    size_t non_moving_space_bytes_allocated = 0U;    size_t bytes_allocated = 0U;    size_t dummy; +  bool fall_back_to_non_moving = false;    mirror::Object* to_ref = region_space_->AllocNonvirtual</*kForEvac*/ true>(        region_space_alloc_size, ®ion_space_bytes_allocated, nullptr, &dummy);    bytes_allocated = region_space_bytes_allocated; -  if (to_ref != nullptr) { +  if (LIKELY(to_ref != nullptr)) {      DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated); -  } -  bool fall_back_to_non_moving = false; -  if (UNLIKELY(to_ref == nullptr)) { +  } else {      // Failed to allocate in the region space. Try the skipped blocks.      to_ref = AllocateInSkippedBlock(region_space_alloc_size);      if (to_ref != nullptr) { @@ -2374,6 +2373,9 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,          region_space_->RecordAlloc(to_ref);        }        bytes_allocated = region_space_alloc_size; +      heap_->num_bytes_allocated_.fetch_sub(bytes_allocated, std::memory_order_seq_cst); +      to_space_bytes_skipped_.fetch_sub(bytes_allocated, std::memory_order_seq_cst); +      to_space_objects_skipped_.fetch_sub(1, std::memory_order_seq_cst);      } else {        // Fall back to the non-moving space.        fall_back_to_non_moving = true; @@ -2383,7 +2385,6 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,                    << " skipped_objects="                    << to_space_objects_skipped_.load(std::memory_order_seq_cst);        } -      fall_back_to_non_moving = true;        to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size,                                                 &non_moving_space_bytes_allocated, nullptr, &dummy);        if (UNLIKELY(to_ref == nullptr)) { @@ -2471,7 +2472,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,      // Do a fence to prevent the field CAS in ConcurrentCopying::Process from possibly reordering      // before the object copy. -    QuasiAtomic::ThreadFenceRelease(); +    std::atomic_thread_fence(std::memory_order_release);      LockWord new_lock_word = LockWord::FromForwardingAddress(reinterpret_cast<size_t>(to_ref)); @@ -2566,7 +2567,7 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) {  bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) {    // TODO: Explain why this is here. What release operation does it pair with? -  QuasiAtomic::ThreadFenceAcquire(); +  std::atomic_thread_fence(std::memory_order_acquire);    accounting::ObjectStack* alloc_stack = GetAllocationStack();    return alloc_stack->Contains(ref);  } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 52afb3850c..f16138c058 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2753,12 +2753,10 @@ class ScanVisitor {  // Verify a reference from an object.  class VerifyReferenceVisitor : public SingleRootVisitor {   public: -  VerifyReferenceVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent) +  VerifyReferenceVisitor(Thread* self, Heap* heap, size_t* fail_count, bool verify_referent)        REQUIRES_SHARED(Locks::mutator_lock_) -      : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} - -  size_t GetFailureCount() const { -    return fail_count_->load(std::memory_order_seq_cst); +      : self_(self), heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) { +    CHECK_EQ(self_, Thread::Current());    }    void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, ObjPtr<mirror::Reference> ref) const @@ -2810,8 +2808,10 @@ class VerifyReferenceVisitor : public SingleRootVisitor {        // Verify that the reference is live.        return true;      } -    if (fail_count_->fetch_add(1, std::memory_order_seq_cst) == 0) { -      // Print message on only on first failure to prevent spam. +    CHECK_EQ(self_, Thread::Current());  // fail_count_ is private to the calling thread. +    *fail_count_ += 1; +    if (*fail_count_ == 1) { +      // Only print message for the first failure to prevent spam.        LOG(ERROR) << "!!!!!!!!!!!!!!Heap corruption detected!!!!!!!!!!!!!!!!!!!";      }      if (obj != nullptr) { @@ -2897,38 +2897,41 @@ class VerifyReferenceVisitor : public SingleRootVisitor {      return false;    } +  Thread* const self_;    Heap* const heap_; -  Atomic<size_t>* const fail_count_; +  size_t* const fail_count_;    const bool verify_referent_;  };  // Verify all references within an object, for use with HeapBitmap::Visit.  class VerifyObjectVisitor {   public: -  VerifyObjectVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent) -      : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} +  VerifyObjectVisitor(Thread* self, Heap* heap, size_t* fail_count, bool verify_referent) +      : self_(self), heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {}    void operator()(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {      // Note: we are verifying the references in obj but not obj itself, this is because obj must      // be live or else how did we find it in the live bitmap? -    VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_); +    VerifyReferenceVisitor visitor(self_, heap_, fail_count_, verify_referent_);      // The class doesn't count as a reference but we should verify it anyways.      obj->VisitReferences(visitor, visitor);    }    void VerifyRoots() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_) {      ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); -    VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_); +    VerifyReferenceVisitor visitor(self_, heap_, fail_count_, verify_referent_);      Runtime::Current()->VisitRoots(&visitor);    } -  size_t GetFailureCount() const { -    return fail_count_->load(std::memory_order_seq_cst); +  uint32_t GetFailureCount() const REQUIRES(Locks::mutator_lock_) { +    CHECK_EQ(self_, Thread::Current()); +    return *fail_count_;    }   private: +  Thread* const self_;    Heap* const heap_; -  Atomic<size_t>* const fail_count_; +  size_t* const fail_count_;    const bool verify_referent_;  }; @@ -2980,8 +2983,8 @@ size_t Heap::VerifyHeapReferences(bool verify_referents) {    // Since we sorted the allocation stack content, need to revoke all    // thread-local allocation stacks.    RevokeAllThreadLocalAllocationStacks(self); -  Atomic<size_t> fail_count_(0); -  VerifyObjectVisitor visitor(this, &fail_count_, verify_referents); +  size_t fail_count = 0; +  VerifyObjectVisitor visitor(self, this, &fail_count, verify_referents);    // Verify objects in the allocation stack since these will be objects which were:    // 1. Allocated prior to the GC (pre GC verification).    // 2. Allocated during the GC (pre sweep GC verification). @@ -3727,13 +3730,21 @@ void Heap::RequestTrim(Thread* self) {    task_processor_->AddTask(self, added_task);  } +void Heap::IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke) { +  size_t previous_num_bytes_freed_revoke = +      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); +  // Check the updated value is less than the number of bytes allocated. There is a risk of +  // execution being suspended between the increment above and the CHECK below, leading to +  // the use of previous_num_bytes_freed_revoke in the comparison. +  CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), +           previous_num_bytes_freed_revoke + freed_bytes_revoke); +} +  void Heap::RevokeThreadLocalBuffers(Thread* thread) {    if (rosalloc_space_ != nullptr) {      size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread);      if (freed_bytes_revoke > 0U) { -      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); -      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), -               num_bytes_freed_revoke_.load(std::memory_order_relaxed)); +      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);      }    }    if (bump_pointer_space_ != nullptr) { @@ -3748,9 +3759,7 @@ void Heap::RevokeRosAllocThreadLocalBuffers(Thread* thread) {    if (rosalloc_space_ != nullptr) {      size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread);      if (freed_bytes_revoke > 0U) { -      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); -      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), -               num_bytes_freed_revoke_.load(std::memory_order_relaxed)); +      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);      }    }  } @@ -3759,9 +3768,7 @@ void Heap::RevokeAllThreadLocalBuffers() {    if (rosalloc_space_ != nullptr) {      size_t freed_bytes_revoke = rosalloc_space_->RevokeAllThreadLocalBuffers();      if (freed_bytes_revoke > 0U) { -      num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); -      CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), -               num_bytes_freed_revoke_.load(std::memory_order_relaxed)); +      IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);      }    }    if (bump_pointer_space_ != nullptr) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 9af57d17e5..ef1c0887bb 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -1091,6 +1091,8 @@ class Heap {      return max_free_;    } +  ALWAYS_INLINE void IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke); +    void TraceHeapSize(size_t heap_size);    // Remove a vlog code from heap-inl.h which is transitively included in half the world. diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc new file mode 100644 index 0000000000..f0b36a090a --- /dev/null +++ b/runtime/hidden_api.cc @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hidden_api.h" + +#include "base/dumpable.h" + +namespace art { +namespace hiddenapi { + +static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { +  switch (value) { +    case kReflection: +      os << "reflection"; +      break; +    case kJNI: +      os << "JNI"; +      break; +    case kLinking: +      os << "linking"; +      break; +  } +  return os; +} + +static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { +  return static_cast<int>(policy) == static_cast<int>(apiList); +} + +// GetMemberAction-related static_asserts. +static_assert( +    EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && +    EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && +    EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), +    "Mismatch between EnforcementPolicy and ApiList enums"); +static_assert( +    EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && +    EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, +    "EnforcementPolicy values ordering not correct"); + +namespace detail { + +MemberSignature::MemberSignature(ArtField* field) { +  member_type_ = "field"; +  signature_parts_ = { +    field->GetDeclaringClass()->GetDescriptor(&tmp_), +    "->", +    field->GetName(), +    ":", +    field->GetTypeDescriptor() +  }; +} + +MemberSignature::MemberSignature(ArtMethod* method) { +  member_type_ = "method"; +  signature_parts_ = { +    method->GetDeclaringClass()->GetDescriptor(&tmp_), +    "->", +    method->GetName(), +    method->GetSignature().ToString() +  }; +} + +bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const { +  size_t pos = 0; +  for (const std::string& part : signature_parts_) { +    size_t count = std::min(prefix.length() - pos, part.length()); +    if (prefix.compare(pos, count, part, 0, count) == 0) { +      pos += count; +    } else { +      return false; +    } +  } +  // We have a complete match if all parts match (we exit the loop without +  // returning) AND we've matched the whole prefix. +  return pos == prefix.length(); +} + +bool MemberSignature::IsExempted(const std::vector<std::string>& exemptions) { +  for (const std::string& exemption : exemptions) { +    if (DoesPrefixMatch(exemption)) { +      return true; +    } +  } +  return false; +} + +void MemberSignature::Dump(std::ostream& os) const { +  for (std::string part : signature_parts_) { +    os << part; +  } +} + +void MemberSignature::WarnAboutAccess(AccessMethod access_method, +                                      HiddenApiAccessFlags::ApiList list) { +  LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this) +               << " (" << list << ", " << access_method << ")"; +} + +template<typename T> +bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) { +  // Get the signature, we need it later. +  MemberSignature member_signature(member); + +  Runtime* runtime = Runtime::Current(); + +  if (action == kDeny) { +    // If we were about to deny, check for an exemption first. +    // Exempted APIs are treated as light grey list. +    if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { +      action = kAllowButWarn; +      // Avoid re-examining the exemption list next time. +      // Note this results in the warning below showing "light greylist", which +      // seems like what one would expect. Exemptions effectively add new members to +      // the light greylist. +      member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( +              member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist)); +    } +  } + +  // Print a log message with information about this class member access. +  // We do this regardless of whether we block the access or not. +  member_signature.WarnAboutAccess(access_method, +      HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + +  if (action == kDeny) { +    // Block access +    return true; +  } + +  // Allow access to this member but print a warning. +  DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); + +  // Depending on a runtime flag, we might move the member into whitelist and +  // skip the warning the next time the member is accessed. +  if (runtime->ShouldDedupeHiddenApiWarnings()) { +    member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( +        member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); +  } + +  // If this action requires a UI warning, set the appropriate flag. +  if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { +    runtime->SetPendingHiddenApiWarning(true); +  } + +  return false; +} + +// Need to instantiate this. +template bool ShouldBlockAccessToMemberImpl<ArtField>(ArtField* member, +                                                      Action action, +                                                      AccessMethod access_method); +template bool ShouldBlockAccessToMemberImpl<ArtMethod>(ArtMethod* member, +                                                       Action action, +                                                       AccessMethod access_method); + +}  // namespace detail +}  // namespace hiddenapi +}  // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 5c6b4b56bc..cc6c146f00 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -19,6 +19,7 @@  #include "art_field-inl.h"  #include "art_method-inl.h" +#include "base/mutex.h"  #include "dex/hidden_api_access_flags.h"  #include "mirror/class-inl.h"  #include "reflection.h" @@ -57,25 +58,6 @@ enum AccessMethod {    kLinking,  }; -inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { -  switch (value) { -    case kReflection: -      os << "reflection"; -      break; -    case kJNI: -      os << "JNI"; -      break; -    case kLinking: -      os << "linking"; -      break; -  } -  return os; -} - -static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { -  return static_cast<int>(policy) == static_cast<int>(apiList); -} -  inline Action GetMemberAction(uint32_t access_flags) {    EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();    if (policy == EnforcementPolicy::kNoChecks) { @@ -88,16 +70,7 @@ inline Action GetMemberAction(uint32_t access_flags) {      return kAllow;    }    // The logic below relies on equality of values in the enums EnforcementPolicy and -  // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected. -  static_assert( -      EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && -      EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && -      EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), -      "Mismatch between EnforcementPolicy and ApiList enums"); -  static_assert( -      EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && -      EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, -      "EnforcementPolicy values ordering not correct"); +  // HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc.    if (static_cast<int>(policy) > static_cast<int>(api_list)) {      return api_list == HiddenApiAccessFlags::kDarkGreylist          ? kAllowButWarnAndToast @@ -107,38 +80,67 @@ inline Action GetMemberAction(uint32_t access_flags) {    }  } -// Issue a warning about field access. -inline void WarnAboutMemberAccess(ArtField* field, AccessMethod access_method) -    REQUIRES_SHARED(Locks::mutator_lock_) { -  std::string tmp; -  LOG(WARNING) << "Accessing hidden field " -               << field->GetDeclaringClass()->GetDescriptor(&tmp) << "->" -               << field->GetName() << ":" << field->GetTypeDescriptor() -               << " (" << HiddenApiAccessFlags::DecodeFromRuntime(field->GetAccessFlags()) -               << ", " << access_method << ")"; -} +// Implementation details. DO NOT ACCESS DIRECTLY. +namespace detail { + +// Class to encapsulate the signature of a member (ArtField or ArtMethod). This +// is used as a helper when matching prefixes, and when logging the signature. +class MemberSignature { + private: +  std::string member_type_; +  std::vector<std::string> signature_parts_; +  std::string tmp_; -// Issue a warning about method access. -inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method) + public: +  explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); +  explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + +  void Dump(std::ostream& os) const; + +  // Performs prefix match on this member. Since the full member signature is +  // composed of several parts, we match each part in turn (rather than +  // building the entire thing in memory and performing a simple prefix match) +  bool DoesPrefixMatch(const std::string& prefix) const; + +  bool IsExempted(const std::vector<std::string>& exemptions); + +  void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list); +}; + +template<typename T> +bool ShouldBlockAccessToMemberImpl(T* member, +                                   Action action, +                                   AccessMethod access_method) +    REQUIRES_SHARED(Locks::mutator_lock_); + +// Returns true if the caller is either loaded by the boot strap class loader or comes from +// a dex file located in ${ANDROID_ROOT}/framework/. +ALWAYS_INLINE +inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader, +                                  ObjPtr<mirror::DexCache> caller_dex_cache)      REQUIRES_SHARED(Locks::mutator_lock_) { -  std::string tmp; -  LOG(WARNING) << "Accessing hidden method " -               << method->GetDeclaringClass()->GetDescriptor(&tmp) << "->" -               << method->GetName() << method->GetSignature().ToString() -               << " (" << HiddenApiAccessFlags::DecodeFromRuntime(method->GetAccessFlags()) -               << ", " << access_method << ")"; +  if (caller_class_loader.IsNull()) { +    return true; +  } else if (caller_dex_cache.IsNull()) { +    return false; +  } else { +    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); +    return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); +  }  } +}  // namespace detail +  // Returns true if access to `member` should be denied to the caller of the -// reflective query. The decision is based on whether the caller is in boot -// class path or not. Because different users of this function determine this -// in a different way, `fn_caller_in_boot(self)` is called and should return -// true if the caller is in boot class path. +// reflective query. The decision is based on whether the caller is in the +// platform or not. Because different users of this function determine this +// in a different way, `fn_caller_in_platform(self)` is called and should +// return true if the caller is located in the platform.  // This function might print warnings into the log if the member is hidden.  template<typename T>  inline bool ShouldBlockAccessToMember(T* member,                                        Thread* self, -                                      std::function<bool(Thread*)> fn_caller_in_boot, +                                      std::function<bool(Thread*)> fn_caller_in_platform,                                        AccessMethod access_method)      REQUIRES_SHARED(Locks::mutator_lock_) {    DCHECK(member != nullptr); @@ -149,42 +151,21 @@ inline bool ShouldBlockAccessToMember(T* member,      return false;    } -  // Member is hidden. Walk the stack to find the caller. +  // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.    // This can be *very* expensive. Save it for last. -  if (fn_caller_in_boot(self)) { -    // Caller in boot class path. Exit. +  if (fn_caller_in_platform(self)) { +    // Caller in the platform. Exit.      return false;    } -  // Member is hidden and we are not in the boot class path. - -  // Print a log message with information about this class member access. -  // We do this regardless of whether we block the access or not. -  WarnAboutMemberAccess(member, access_method); - -  if (action == kDeny) { -    // Block access -    return true; -  } - -  // Allow access to this member but print a warning. -  DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); - -  Runtime* runtime = Runtime::Current(); - -  // Depending on a runtime flag, we might move the member into whitelist and -  // skip the warning the next time the member is accessed. -  if (runtime->ShouldDedupeHiddenApiWarnings()) { -    member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( -        member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); -  } - -  // If this action requires a UI warning, set the appropriate flag. -  if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { -    runtime->SetPendingHiddenApiWarning(true); -  } +  // Member is hidden and caller is not in the platform. +  return detail::ShouldBlockAccessToMemberImpl(member, action, access_method); +} -  return false; +inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller) +    REQUIRES_SHARED(Locks::mutator_lock_) { +  return !caller.IsNull() && +      detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());  }  // Returns true if access to `member` should be denied to a caller loaded with @@ -193,12 +174,13 @@ inline bool ShouldBlockAccessToMember(T* member,  template<typename T>  inline bool ShouldBlockAccessToMember(T* member,                                        ObjPtr<mirror::ClassLoader> caller_class_loader, +                                      ObjPtr<mirror::DexCache> caller_dex_cache,                                        AccessMethod access_method)      REQUIRES_SHARED(Locks::mutator_lock_) { -  bool caller_in_boot = (caller_class_loader.IsNull()); +  bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);    return ShouldBlockAccessToMember(member,                                     /* thread */ nullptr, -                                   [caller_in_boot] (Thread*) { return caller_in_boot; }, +                                   [caller_in_platform] (Thread*) { return caller_in_platform; },                                     access_method);  } diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc new file mode 100644 index 0000000000..5a31dd4972 --- /dev/null +++ b/runtime/hidden_api_test.cc @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hidden_api.h" + +#include "common_runtime_test.h" +#include "jni_internal.h" + +namespace art { + +using hiddenapi::detail::MemberSignature; + +class HiddenApiTest : public CommonRuntimeTest { + protected: +  void SetUp() OVERRIDE { +    // Do the normal setup. +    CommonRuntimeTest::SetUp(); +    self_ = Thread::Current(); +    self_->TransitionFromSuspendedToRunnable(); +    LoadDex("HiddenApiSignatures"); +    bool started = runtime_->Start(); +    CHECK(started); + +    class1_field1_ = getArtField("mypackage/packagea/Class1", "field1", "I"); +    class1_field12_ = getArtField("mypackage/packagea/Class1", "field12", "I"); +    class1_init_ = getArtMethod("mypackage/packagea/Class1", "<init>", "()V"); +    class1_method1_ = getArtMethod("mypackage/packagea/Class1", "method1", "()V"); +    class1_method1_i_ = getArtMethod("mypackage/packagea/Class1", "method1", "(I)V"); +    class1_method12_ = getArtMethod("mypackage/packagea/Class1", "method12", "()V"); +    class12_field1_ = getArtField("mypackage/packagea/Class12", "field1", "I"); +    class12_method1_ = getArtMethod("mypackage/packagea/Class12", "method1", "()V"); +    class2_field1_ = getArtField("mypackage/packagea/Class2", "field1", "I"); +    class2_method1_ = getArtMethod("mypackage/packagea/Class2", "method1", "()V"); +    class2_method1_i_ = getArtMethod("mypackage/packagea/Class2", "method1", "(I)V"); +    class3_field1_ = getArtField("mypackage/packageb/Class3", "field1", "I"); +    class3_method1_ = getArtMethod("mypackage/packageb/Class3", "method1", "()V"); +    class3_method1_i_ = getArtMethod("mypackage/packageb/Class3", "method1", "(I)V"); +  } + +  ArtMethod* getArtMethod(const char* class_name, const char* name, const char* signature) { +    JNIEnv* env = Thread::Current()->GetJniEnv(); +    jclass klass = env->FindClass(class_name); +    jmethodID method_id = env->GetMethodID(klass, name, signature); +    ArtMethod* art_method = jni::DecodeArtMethod(method_id); +    return art_method; +  } + +  ArtField* getArtField(const char* class_name, const char* name, const char* signature) { +    JNIEnv* env = Thread::Current()->GetJniEnv(); +    jclass klass = env->FindClass(class_name); +    jfieldID field_id = env->GetFieldID(klass, name, signature); +    ArtField* art_field = jni::DecodeArtField(field_id); +    return art_field; +  } + + protected: +  Thread* self_; +  ArtField* class1_field1_; +  ArtField* class1_field12_; +  ArtMethod* class1_init_; +  ArtMethod* class1_method1_; +  ArtMethod* class1_method1_i_; +  ArtMethod* class1_method12_; +  ArtField* class12_field1_; +  ArtMethod* class12_method1_; +  ArtField* class2_field1_; +  ArtMethod* class2_method1_; +  ArtMethod* class2_method1_i_; +  ArtField* class3_field1_; +  ArtMethod* class3_method1_; +  ArtMethod* class3_method1_i_; +}; + +TEST_F(HiddenApiTest, CheckMembersRead) { +  ASSERT_NE(nullptr, class1_field1_); +  ASSERT_NE(nullptr, class1_field12_); +  ASSERT_NE(nullptr, class1_init_); +  ASSERT_NE(nullptr, class1_method1_); +  ASSERT_NE(nullptr, class1_method1_i_); +  ASSERT_NE(nullptr, class1_method12_); +  ASSERT_NE(nullptr, class12_field1_); +  ASSERT_NE(nullptr, class12_method1_); +  ASSERT_NE(nullptr, class2_field1_); +  ASSERT_NE(nullptr, class2_method1_); +  ASSERT_NE(nullptr, class2_method1_i_); +  ASSERT_NE(nullptr, class3_field1_); +  ASSERT_NE(nullptr, class3_method1_); +  ASSERT_NE(nullptr, class3_method1_i_); +} + +TEST_F(HiddenApiTest, CheckEverythingMatchesL) { +  ScopedObjectAccess soa(self_); +  std::string prefix("L"); +  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class3_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class3_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckPackageMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/"); +  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class3_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class3_method1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckClassMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1"); +  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckClassExactMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;"); +  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->method1"); +  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodExactMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->method1("); +  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodSignatureMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->method1(I)"); +  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodSignatureAndReturnMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->method1()V"); +  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckFieldMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->field1"); +  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckFieldExactMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->field1:"); +  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckFieldTypeMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->field1:I"); +  ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckConstructorMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;-><init>"); +  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckConstructorExactMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;-><init>()V"); +  ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodSignatureTrailingCharsNoMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->method1()Vfoo"); +  ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckConstructorTrailingCharsNoMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;-><init>()Vfoo"); +  ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckFieldTrailingCharsNoMatch) { +  ScopedObjectAccess soa(self_); +  std::string prefix("Lmypackage/packagea/Class1;->field1:Ifoo"); +  ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +} + +}  // namespace art diff --git a/runtime/image.cc b/runtime/image.cc index 5af3e5451b..f14707874b 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@  namespace art {  const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '7', '\0' };  // R^2 Bitstring type check. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '8', '\0' };  // R^3 Bitstring type check.  ImageHeader::ImageHeader(uint32_t image_begin,                           uint32_t image_size, diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 735c0e815a..f23304c391 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -243,11 +243,13 @@ static inline JValue Execute(      const CodeItemDataAccessor& accessor,      ShadowFrame& shadow_frame,      JValue result_register, -    bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) { +    bool stay_in_interpreter = false, +    bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) {    DCHECK(!shadow_frame.GetMethod()->IsAbstract());    DCHECK(!shadow_frame.GetMethod()->IsNative()); -  if (LIKELY(shadow_frame.GetDexPC() == 0)) {  // Entering the method, but not via deoptimization. +  if (LIKELY(!from_deoptimize)) {  // Entering the method, but not via deoptimization.      if (kIsDebugBuild) { +      CHECK_EQ(shadow_frame.GetDexPC(), 0u);        self->AssertNoPendingException();      }      instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); @@ -568,7 +570,12 @@ void EnterInterpreterFromDeoptimize(Thread* self,      }      if (new_dex_pc != dex::kDexNoIndex) {        shadow_frame->SetDexPC(new_dex_pc); -      value = Execute(self, accessor, *shadow_frame, value); +      value = Execute(self, +                      accessor, +                      *shadow_frame, +                      value, +                      /* stay_in_interpreter */ true, +                      /* from_deoptimize */ true);      }      ShadowFrame* old_frame = shadow_frame;      shadow_frame = shadow_frame->GetLink(); diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc index 681a582b5d..022b1395bf 100644 --- a/runtime/interpreter/interpreter_intrinsics.cc +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -91,7 +91,7 @@ BINARY_II_INTRINSIC(MterpIntegerRotateLeft, (Rot<int32_t, true>), SetI);  // java.lang.Integer.signum(I)I  UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI); -// java.lang.Long.reverse(I)I +// java.lang.Long.reverse(J)J  UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ);  // java.lang.Long.reverseBytes(J)J diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 76df65f730..4c7a97dfa8 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -184,6 +184,7 @@ static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* fram    return hiddenapi::ShouldBlockAccessToMember(        member,        frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), +      frame->GetMethod()->GetDeclaringClass()->GetDexCache(),        hiddenapi::kReflection);  // all uses in this file are from reflection  } @@ -1537,7 +1538,7 @@ void UnstartedRuntime::UnstartedUnsafePutOrderedObject(    }    int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2);    mirror::Object* newValue = shadow_frame->GetVRegReference(arg_offset + 4); -  QuasiAtomic::ThreadFenceRelease(); +  std::atomic_thread_fence(std::memory_order_release);    if (Runtime::Current()->IsActiveTransaction()) {      obj->SetFieldObject<true>(MemberOffset(offset), newValue);    } else { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 1c4b93eb48..de64fdd2c9 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -248,7 +248,6 @@ JitCodeCache::JitCodeCache(MemMap* code_map,        code_end_(initial_code_capacity),        data_end_(initial_data_capacity),        last_collection_increased_code_cache_(false), -      last_update_time_ns_(0),        garbage_collect_code_(garbage_collect_code),        used_memory_for_data_(0),        used_memory_for_code_(0), @@ -820,7 +819,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,        // code.        GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));      } -    last_update_time_ns_.store(NanoTime(), std::memory_order_release);      VLOG(jit)          << "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") "          << ArtMethod::PrettyMethod(method) << "@" << method @@ -1541,7 +1539,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNU    // Make sure other threads see the data in the profiling info object before the    // store in the ArtMethod's ProfilingInfo pointer. -  QuasiAtomic::ThreadFenceRelease(); +  std::atomic_thread_fence(std::memory_order_release);    method->SetProfilingInfo(info);    profiling_infos_.push_back(info); @@ -1646,10 +1644,6 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca    }  } -uint64_t JitCodeCache::GetLastUpdateTimeNs() const { -  return last_update_time_ns_.load(std::memory_order_acquire); -} -  bool JitCodeCache::IsOsrCompiled(ArtMethod* method) {    MutexLock mu(Thread::Current(), lock_);    return osr_code_map_.find(method) != osr_code_map_.end(); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index dfa7ac0970..f1c99fb85a 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -407,10 +407,6 @@ class JitCodeCache {    // Whether the last collection round increased the code cache.    bool last_collection_increased_code_cache_ GUARDED_BY(lock_); -  // Last time the the code_cache was updated. -  // It is atomic to avoid locking when reading it. -  Atomic<uint64_t> last_update_time_ns_; -    // Whether we can do garbage collection. Not 'const' as tests may override this.    bool garbage_collect_code_; diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index b78fcacc08..f309581735 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -80,18 +80,15 @@ namespace art {  // things not rendering correctly. E.g. b/16858794  static constexpr bool kWarnJniAbort = false; -static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { -  ObjPtr<mirror::Class> klass = GetCallingClass(self, /* num_frames */ 1); -  // If `klass` is null, it is an unattached native thread. Assume this is -  // *not* boot class path. -  return klass != nullptr && klass->IsBootStrapClassLoaded(); +static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { +  return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1));  }  template<typename T>  ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)      REQUIRES_SHARED(Locks::mutator_lock_) {    return hiddenapi::ShouldBlockAccessToMember( -      member, self, IsCallerInBootClassPath, hiddenapi::kJNI); +      member, self, IsCallerInPlatformDex, hiddenapi::kJNI);  }  // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index f0898f49d3..72b31790f0 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -31,7 +31,6 @@  #include "dex/invoke_type.h"  #include "dex_cache.h"  #include "gc/heap-inl.h" -#include "hidden_api.h"  #include "iftable.h"  #include "subtype_check.h"  #include "object-inl.h" diff --git a/runtime/monitor.cc b/runtime/monitor.cc index e110763300..f246d8b1c0 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1101,7 +1101,7 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool tr        case LockWord::kFatLocked: {          // We should have done an acquire read of the lockword initially, to ensure          // visibility of the monitor data structure. Use an explicit fence instead. -        QuasiAtomic::ThreadFenceAcquire(); +        std::atomic_thread_fence(std::memory_order_acquire);          Monitor* mon = lock_word.FatLockMonitor();          if (trylock) {            return mon->TryLock(self) ? h_obj.Get() : nullptr; diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 505b745200..a5ade6f30f 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -78,6 +78,21 @@ static jboolean VMRuntime_hasUsedHiddenApi(JNIEnv*, jobject) {    return Runtime::Current()->HasPendingHiddenApiWarning() ? JNI_TRUE : JNI_FALSE;  } +static void VMRuntime_setHiddenApiExemptions(JNIEnv* env, +                                            jclass, +                                            jobjectArray exemptions) { +  std::vector<std::string> exemptions_vec; +  int exemptions_length = env->GetArrayLength(exemptions); +  for (int i = 0; i < exemptions_length; i++) { +    jstring exemption = reinterpret_cast<jstring>(env->GetObjectArrayElement(exemptions, i)); +    const char* raw_exemption = env->GetStringUTFChars(exemption, nullptr); +    exemptions_vec.push_back(raw_exemption); +    env->ReleaseStringUTFChars(exemption, raw_exemption); +  } + +  Runtime::Current()->SetHiddenApiExemptions(exemptions_vec); +} +  static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass,                                              jint length) {    ScopedFastNativeObjectAccess soa(env); @@ -672,6 +687,7 @@ static JNINativeMethod gMethods[] = {    NATIVE_METHOD(VMRuntime, concurrentGC, "()V"),    NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"),    NATIVE_METHOD(VMRuntime, hasUsedHiddenApi, "()Z"), +  NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"),    NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),    FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),    FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"), diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index d9a5096331..cf0a72a477 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -27,6 +27,7 @@  #include "base/mutex.h"  #include "base/runtime_debug.h"  #include "debugger.h" +#include "hidden_api.h"  #include "java_vm_ext.h"  #include "jit/jit.h"  #include "jni_internal.h" diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index fc61c9597e..ad05856eaf 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -49,8 +49,8 @@  namespace art { -// Returns true if the first non-ClassClass caller up the stack is in boot class path. -static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { +// Returns true if the first non-ClassClass caller up the stack is in a platform dex file. +static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {    // Walk the stack and find the first frame not from java.lang.Class.    // This is very expensive. Save this till the last.    struct FirstNonClassClassCallerVisitor : public StackVisitor { @@ -82,7 +82,7 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator    FirstNonClassClassCallerVisitor visitor(self);    visitor.WalkStack();    return visitor.caller != nullptr && -         visitor.caller->GetDeclaringClass()->IsBootStrapClassLoaded(); +         hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass());  }  // Returns true if the first non-ClassClass caller up the stack is not allowed to @@ -90,7 +90,7 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator  ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)      REQUIRES_SHARED(Locks::mutator_lock_) {    hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); -  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self); +  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self);  }  // Returns true if the first non-ClassClass caller up the stack should not be @@ -99,7 +99,7 @@ template<typename T>  ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)      REQUIRES_SHARED(Locks::mutator_lock_) {    return hiddenapi::ShouldBlockAccessToMember( -      member, self, IsCallerInBootClassPath, hiddenapi::kReflection); +      member, self, IsCallerInPlatformDex, hiddenapi::kReflection);  }  // Returns true if a class member should be discoverable with reflection given diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 25f984f6be..fb00ae3967 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -116,7 +116,7 @@ static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong of    ScopedFastNativeObjectAccess soa(env);    ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);    // TODO: A release store is likely to be faster on future processors. -  QuasiAtomic::ThreadFenceRelease(); +  std::atomic_thread_fence(std::memory_order_release);    // JNI must use non transactional mode.    obj->SetField32<false>(MemberOffset(offset), newValue);  } @@ -152,7 +152,7 @@ static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong o                                    jlong newValue) {    ScopedFastNativeObjectAccess soa(env);    ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj); -  QuasiAtomic::ThreadFenceRelease(); +  std::atomic_thread_fence(std::memory_order_release);    // JNI must use non transactional mode.    obj->SetField64<false>(MemberOffset(offset), newValue);  } @@ -194,7 +194,7 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong    ScopedFastNativeObjectAccess soa(env);    ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);    ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue); -  QuasiAtomic::ThreadFenceRelease(); +  std::atomic_thread_fence(std::memory_order_release);    // JNI must use non transactional mode.    obj->SetFieldObject<false>(MemberOffset(offset), newValue);  } diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index c26c26e544..14f3f45f9e 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -287,7 +287,8 @@ void DumpNativeStack(std::ostream& os,                       BacktraceMap* existing_map,                       const char* prefix,                       ArtMethod* current_method, -                     void* ucontext_ptr) { +                     void* ucontext_ptr, +                     bool skip_frames) {    // b/18119146    if (RUNNING_ON_MEMORY_TOOL != 0) {      return; @@ -300,6 +301,7 @@ void DumpNativeStack(std::ostream& os,      map = tmp_map.get();    }    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map)); +  backtrace->SetSkipFrames(skip_frames);    if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {      os << prefix << "(backtrace::Unwind failed for thread " << tid         << ": " <<  backtrace->GetErrorString(backtrace->GetError()) << ")" << std::endl; @@ -427,7 +429,8 @@ void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED,                       BacktraceMap* existing_map ATTRIBUTE_UNUSED,                       const char* prefix ATTRIBUTE_UNUSED,                       ArtMethod* current_method ATTRIBUTE_UNUSED, -                     void* ucontext_ptr ATTRIBUTE_UNUSED) { +                     void* ucontext_ptr ATTRIBUTE_UNUSED, +                     bool skip_frames ATTRIBUTE_UNUSED) {  }  void DumpKernelStack(std::ostream& os ATTRIBUTE_UNUSED, diff --git a/runtime/native_stack_dump.h b/runtime/native_stack_dump.h index d64bc824a5..ad4bfab0e9 100644 --- a/runtime/native_stack_dump.h +++ b/runtime/native_stack_dump.h @@ -35,7 +35,8 @@ void DumpNativeStack(std::ostream& os,                       BacktraceMap* map = nullptr,                       const char* prefix = "",                       ArtMethod* current_method = nullptr, -                     void* ucontext = nullptr) +                     void* ucontext = nullptr, +                     bool skip_frames = true)      NO_THREAD_SAFETY_ANALYSIS;  // Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86. diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5518eb2c49..470287b449 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -161,10 +161,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize        .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})            .WithValues({true, false})            .IntoKey(M::EnableHSpaceCompactForOOM) -      .Define("-XX:DumpNativeStackOnSigQuit:_") -          .WithType<bool>() -          .WithValueMap({{"false", false}, {"true", true}}) -          .IntoKey(M::DumpNativeStackOnSigQuit)        .Define("-XX:MadviseRandomAccess:_")            .WithType<bool>()            .WithValueMap({{"false", false}, {"true", true}}) @@ -735,7 +731,6 @@ void ParsedOptions::Usage(const char* fmt, ...) {    UsageMessage(stream, "  -XX:BackgroundGC=none\n");    UsageMessage(stream, "  -XX:LargeObjectSpace={disabled,map,freelist}\n");    UsageMessage(stream, "  -XX:LargeObjectThreshold=N\n"); -  UsageMessage(stream, "  -XX:DumpNativeStackOnSigQuit=booleanvalue\n");    UsageMessage(stream, "  -XX:MadviseRandomAccess:booleanvalue\n");    UsageMessage(stream, "  -XX:SlowDebug={false,true}\n");    UsageMessage(stream, "  -Xmethod-trace\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 9a626bab00..00ccc19b5d 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -86,6 +86,7 @@  #include "gc/space/space-inl.h"  #include "gc/system_weak.h"  #include "handle_scope-inl.h" +#include "hidden_api.h"  #include "image-inl.h"  #include "instrumentation.h"  #include "intern_table.h" @@ -271,7 +272,6 @@ Runtime::Runtime()        pending_hidden_api_warning_(false),        dedupe_hidden_api_warnings_(true),        always_set_hidden_api_warning_flag_(false), -      dump_native_stack_on_sig_quit_(true),        pruned_dalvik_cache_(false),        // Initially assume we perceive jank in case the process state is never updated.        process_state_(kProcessStateJankPerceptible), @@ -1153,7 +1153,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {    is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);    dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat);    image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat); -  dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit);    vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf);    exit_ = runtime_options.GetOrDefault(Opt::HookExit); diff --git a/runtime/runtime.h b/runtime/runtime.h index dba31b2939..f5f3e31656 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -536,6 +536,14 @@ class Runtime {      pending_hidden_api_warning_ = value;    } +  void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) { +    hidden_api_exemptions_ = exemptions; +  } + +  const std::vector<std::string>& GetHiddenApiExemptions() { +    return hidden_api_exemptions_; +  } +    bool HasPendingHiddenApiWarning() const {      return pending_hidden_api_warning_;    } @@ -655,10 +663,6 @@ class Runtime {      safe_mode_ = mode;    } -  bool GetDumpNativeStackOnSigQuit() const { -    return dump_native_stack_on_sig_quit_; -  } -    bool GetPrunedDalvikCache() const {      return pruned_dalvik_cache_;    } @@ -996,6 +1000,9 @@ class Runtime {    // Whether access checks on hidden API should be performed.    hiddenapi::EnforcementPolicy hidden_api_policy_; +  // List of signature prefixes of methods that have been removed from the blacklist +  std::vector<std::string> hidden_api_exemptions_; +    // Whether the application has used an API which is not restricted but we    // should issue a warning about it.    bool pending_hidden_api_warning_; @@ -1009,9 +1016,6 @@ class Runtime {    // when there is a warning. This is only used for testing.    bool always_set_hidden_api_warning_flag_; -  // Whether threads should dump their native stack on SIGQUIT. -  bool dump_native_stack_on_sig_quit_; -    // Whether the dalvik cache was pruned when initializing the runtime.    bool pruned_dalvik_cache_; diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index 59af9187f9..41bfb58d93 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -41,7 +41,6 @@ namespace art {  using android::base::StringPrintf;  static constexpr bool kUseSigRTTimeout = true; -static constexpr bool kDumpNativeStackOnTimeout = true;  const char* GetSignalName(int signal_number) {    switch (signal_number) { @@ -441,7 +440,7 @@ void HandleUnexpectedSignalCommon(int signal_number,        // Special timeout signal. Try to dump all threads.        // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts        //       are of value here. -      runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); +      runtime->GetThreadList()->Dump(std::cerr);        std::cerr << std::endl;      } diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h index 3fba441b55..698d0603ee 100644 --- a/runtime/runtime_common.h +++ b/runtime/runtime_common.h @@ -40,7 +40,9 @@ struct Backtrace {   public:    explicit Backtrace(void* raw_context) : raw_context_(raw_context) {}    void Dump(std::ostream& os) const { -    DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_); +    // This is a backtrace from a crash, do not skip any frames in case the +    // crash is in the unwinder itself. +    DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_, false);    }   private:    // Stores the context of the signal that was unexpected and will terminate the runtime. The diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 4121ad69ed..dcb1335023 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -70,7 +70,6 @@ RUNTIME_OPTIONS_KEY (Unit,                LowMemoryMode)  RUNTIME_OPTIONS_KEY (bool,                UseTLAB,                        (kUseTlab || kUseReadBarrier))  RUNTIME_OPTIONS_KEY (bool,                EnableHSpaceCompactForOOM,      true)  RUNTIME_OPTIONS_KEY (bool,                UseJitCompilation,              false) -RUNTIME_OPTIONS_KEY (bool,                DumpNativeStackOnSigQuit,       true)  RUNTIME_OPTIONS_KEY (bool,                MadviseRandomAccess,            false)  RUNTIME_OPTIONS_KEY (unsigned int,        JITCompileThreshold)  RUNTIME_OPTIONS_KEY (unsigned int,        JITWarmupThreshold) diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 3b1d5f8c4a..1fe62e8f46 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -286,6 +286,17 @@ struct SubtypeCheck {      return SubtypeCheckInfo::kUninitialized;    } +  // Retrieve the state of this class's SubtypeCheckInfo. +  // +  // Cost: O(Depth(Class)). +  // +  // Returns: The precise SubtypeCheckInfo::State. +  static SubtypeCheckInfo::State GetState(ClassPtr klass) +      REQUIRES(Locks::subtype_check_lock_) +      REQUIRES_SHARED(Locks::mutator_lock_) { +    return GetSubtypeCheckInfo(klass).GetState(); +  } +    // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to    // be used by a fast check "encoded_src & mask_target == encoded_target".    // @@ -308,8 +319,9 @@ struct SubtypeCheck {    static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass)        REQUIRES(Locks::subtype_check_lock_)        REQUIRES_SHARED(Locks::mutator_lock_) { -    DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); -    return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot(); +    SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); +    DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); +    return sci.GetEncodedPathToRoot();    }    // Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to @@ -321,8 +333,9 @@ struct SubtypeCheck {    static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass)        REQUIRES(Locks::subtype_check_lock_)        REQUIRES_SHARED(Locks::mutator_lock_) { -    DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); -    return GetSubtypeCheckInfo(klass).GetEncodedPathToRootMask(); +    SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); +    DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); +    return sci.GetEncodedPathToRootMask();    }    // Is the source class a subclass of the target? diff --git a/runtime/thread.cc b/runtime/thread.cc index b13d8ec42a..50cf9e0bc4 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1161,10 +1161,9 @@ void Thread::ShortDump(std::ostream& os) const {       << "]";  } -void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map, -                  bool force_dump_stack) const { +void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const {    DumpState(os); -  DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack); +  DumpStack(os, backtrace_map, force_dump_stack);  }  mirror::String* Thread::GetThreadName() const { @@ -1968,10 +1967,7 @@ void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_loc    }  } -void Thread::DumpStack(std::ostream& os, -                       bool dump_native_stack, -                       BacktraceMap* backtrace_map, -                       bool force_dump_stack) const { +void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const {    // TODO: we call this code when dying but may not have suspended the thread ourself. The    //       IsSuspended check is therefore racy with the use for dumping (normally we inhibit    //       the race with the thread_suspend_count_lock_). @@ -1984,7 +1980,7 @@ void Thread::DumpStack(std::ostream& os,    }    if (safe_to_dump || force_dump_stack) {      // If we're currently in native code, dump that stack before dumping the managed stack. -    if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) { +    if (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this)) {        DumpKernelStack(os, GetTid(), "  kernel: ", false);        ArtMethod* method =            GetCurrentMethod(nullptr, diff --git a/runtime/thread.h b/runtime/thread.h index 22b77eea64..af1401ee93 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -207,7 +207,6 @@ class Thread {    // Dumps the detailed thread state and the thread stack (used for SIGQUIT).    void Dump(std::ostream& os, -            bool dump_native_stack = true,              BacktraceMap* backtrace_map = nullptr,              bool force_dump_stack = false) const        REQUIRES(!Locks::thread_suspend_count_lock_) @@ -1318,7 +1317,6 @@ class Thread {    void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_);    void DumpStack(std::ostream& os, -                 bool dump_native_stack = true,                   BacktraceMap* backtrace_map = nullptr,                   bool force_dump_stack = false) const        REQUIRES(!Locks::thread_suspend_count_lock_) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 44af867d60..ee683992ba 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -152,9 +152,8 @@ void ThreadList::DumpForSigQuit(std::ostream& os) {        suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data);  // Dump time to suspend.      }    } -  bool dump_native_stack = Runtime::Current()->GetDumpNativeStackOnSigQuit(); -  Dump(os, dump_native_stack); -  DumpUnattachedThreads(os, dump_native_stack && kDumpUnattachedThreadNativeStackForSigQuit); +  Dump(os); +  DumpUnattachedThreads(os, kDumpUnattachedThreadNativeStackForSigQuit);  }  static void DumpUnattachedThread(std::ostream& os, pid_t tid, bool dump_native_stack) @@ -201,11 +200,10 @@ static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000;  // A closure used by Thread::Dump.  class DumpCheckpoint FINAL : public Closure {   public: -  DumpCheckpoint(std::ostream* os, bool dump_native_stack) +  explicit DumpCheckpoint(std::ostream* os)        : os_(os),          barrier_(0), -        backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr), -        dump_native_stack_(dump_native_stack) { +        backtrace_map_(BacktraceMap::Create(getpid())) {      if (backtrace_map_ != nullptr) {        backtrace_map_->SetSuffixesToIgnore(std::vector<std::string> { "oat", "odex" });      } @@ -219,7 +217,7 @@ class DumpCheckpoint FINAL : public Closure {      std::ostringstream local_os;      {        ScopedObjectAccess soa(self); -      thread->Dump(local_os, dump_native_stack_, backtrace_map_.get()); +      thread->Dump(local_os, backtrace_map_.get());      }      {        // Use the logging lock to ensure serialization when writing to the common ostream. @@ -247,18 +245,16 @@ class DumpCheckpoint FINAL : public Closure {    Barrier barrier_;    // A backtrace map, so that all threads use a shared info and don't reacquire/parse separately.    std::unique_ptr<BacktraceMap> backtrace_map_; -  // Whether we should dump the native stack. -  const bool dump_native_stack_;  }; -void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { +void ThreadList::Dump(std::ostream& os) {    Thread* self = Thread::Current();    {      MutexLock mu(self, *Locks::thread_list_lock_);      os << "DALVIK THREADS (" << list_.size() << "):\n";    }    if (self != nullptr) { -    DumpCheckpoint checkpoint(&os, dump_native_stack); +    DumpCheckpoint checkpoint(&os);      size_t threads_running_checkpoint;      {        // Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time. @@ -269,7 +265,7 @@ void ThreadList::Dump(std::ostream& os, bool dump_native_stack) {        checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);      }    } else { -    DumpUnattachedThreads(os, dump_native_stack); +    DumpUnattachedThreads(os, /* dump_native_stack */ true);    }  } @@ -491,7 +487,6 @@ void ThreadList::RunEmptyCheckpoint() {                // Found a runnable thread that hasn't responded to the empty checkpoint request.                // Assume it's stuck and safe to dump its stack.                thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), -                           /*dump_native_stack*/ true,                             /*backtrace_map*/ nullptr,                             /*force_dump_stack*/ true);              } diff --git a/runtime/thread_list.h b/runtime/thread_list.h index 895c1a41ce..09b10d2ad3 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -57,7 +57,7 @@ class ThreadList {    void DumpForSigQuit(std::ostream& os)        REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_);    // For thread suspend timeout dumps. -  void Dump(std::ostream& os, bool dump_native_stack = true) +  void Dump(std::ostream& os)        REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);    pid_t GetLockOwner();  // For SignalCatcher. diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java index ff6186b240..51d4a81150 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -70,15 +70,17 @@ public class Main {          return s[0];      } -    private static void printWeakReference(WeakReference<FinalizerTest> wimp) { +    public static void main(String[] args) { +        WeakReference<FinalizerTest> wimp = makeRef();          // Reference ft so we are sure the WeakReference cannot be cleared. +        // Note: This is very fragile. It was previously in a helper function but that +        // doesn't work for JIT-on-first-use with --gcstress where the object would be +        // collected when JIT internally allocates an array. Also adding a scope around +        // the keepLive lifetime somehow keeps a non-null `keepLive` around and makes +        // the test fail (even when keeping the `null` assignment). b/76454261          FinalizerTest keepLive = wimp.get();          System.out.println("wimp: " + wimpString(wimp)); -    } - -    public static void main(String[] args) { -        WeakReference<FinalizerTest> wimp = makeRef(); -        printWeakReference(wimp); +        keepLive = null;  // Clear the reference.          /* this will try to collect and finalize ft */          System.out.println("gc"); diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check deleted file mode 100755 index 91966b41a0..0000000000 --- a/test/1940-ddms-ext/check +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -#     http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Need to pull out the describeException ouput since that won't be there on -# device. -./remove_error.py "$2" "./expected_error.txt" > "$2.tmp" - -./default-check "$1" "$2.tmp" diff --git a/test/1940-ddms-ext/expected_error.txt b/test/1940-ddms-ext/expected_error.txt deleted file mode 100644 index 73883b46e2..0000000000 --- a/test/1940-ddms-ext/expected_error.txt +++ /dev/null @@ -1,4 +0,0 @@ -java.lang.ArrayIndexOutOfBoundsException: byte[] offset=12 length=55 src.length=1 -	at art.Test1940.processChunk(Native Method) -	at art.Test1940.run(Test1940.java:156) -	at Main.main(Main.java:19) diff --git a/test/1940-ddms-ext/remove_error.py b/test/1940-ddms-ext/remove_error.py deleted file mode 100755 index 638c479a31..0000000000 --- a/test/1940-ddms-ext/remove_error.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -#     http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse - -def main(): -  parser = argparse.ArgumentParser() -  parser.add_argument('input_data', type=open) -  parser.add_argument('expected_error', type=str) -  args = parser.parse_args() - -  for line in map(str.rstrip, args.input_data.readlines()): -    print_full = True -    with open(args.expected_error) as err_file: -      for err_line in map(str.rstrip, err_file): -        if line.startswith(err_line): -          print_full = False -          if line != err_line: -            print(line[len(err_line):]) -          break -    if print_full and line != '': -      print(line) - -if __name__ == '__main__': -  main() diff --git a/test/411-optimizing-arith-mul/info.txt b/test/411-optimizing-arith-mul/info.txt deleted file mode 100644 index 10155512f0..0000000000 --- a/test/411-optimizing-arith-mul/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for basic arithmethic operations. diff --git a/test/411-optimizing-arith-mul/expected.txt b/test/411-optimizing-arith/expected.txt index e69de29bb2..e69de29bb2 100644 --- a/test/411-optimizing-arith-mul/expected.txt +++ b/test/411-optimizing-arith/expected.txt diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith/info.txt new file mode 100644 index 0000000000..42be5d564f --- /dev/null +++ b/test/411-optimizing-arith/info.txt @@ -0,0 +1,7 @@ +Tests for basic arithmethic operations: +  - multiply, +  - subtract, +  - negate, +  - division, +  - modulo (rem), +  - shifts. diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/411-optimizing-arith/src/DivTest.java index 68e89b3eb2..7696d0a806 100644 --- a/test/417-optimizing-arith-div/src/Main.java +++ b/test/411-optimizing-arith/src/DivTest.java @@ -16,7 +16,7 @@  // Note that $opt$ is a marker for the optimizing compiler to test  // it does compile the method. -public class Main { +public class DivTest {    public static void expectEquals(int expected, int result) {      if (expected != result) { @@ -98,11 +98,7 @@ public class Main {      }    } -  public static void main(String[] args) { -    div(); -  } - -  public static void div() { +  public static void main() {      divInt();      divLong();      divFloat(); diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith/src/Main.java new file mode 100644 index 0000000000..e1a43d3b57 --- /dev/null +++ b/test/411-optimizing-arith/src/Main.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { +    public static void main(String args[]) { +        MulTest.main(); +        SubTest.main(); +        NegTest.main(); +        DivTest.main(); +        RemTest.main(); +        ShiftsTest.main(); +    } +} diff --git a/test/411-optimizing-arith-mul/src/Main.java b/test/411-optimizing-arith/src/MulTest.java index 60e418e1e5..b9bffca0d1 100644 --- a/test/411-optimizing-arith-mul/src/Main.java +++ b/test/411-optimizing-arith/src/MulTest.java @@ -16,7 +16,7 @@  // Note that $opt$ is a marker for the optimizing compiler to test  // it does compile the method. -public class Main { +public class MulTest {    public static void expectEquals(int expected, int result) {      if (expected != result) { @@ -72,11 +72,7 @@ public class Main {      }    } -  public static void main(String[] args) { -    mul(); -  } - -  public static void mul() { +  public static void main() {      mulInt();      mulLong();      mulFloat(); @@ -129,9 +125,12 @@ public class Main {      expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, 3.40282346638528860e+38F));      expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(2F, Float.NEGATIVE_INFINITY));      expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(-2F, Float.NEGATIVE_INFINITY)); -    expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)); -    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); -    expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)); +    expectEquals(Float.NEGATIVE_INFINITY, +                 $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)); +    expectEquals(Float.POSITIVE_INFINITY, +                 $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); +    expectEquals(Float.POSITIVE_INFINITY, +                 $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));    }    private static void mulDouble() { diff --git a/test/415-optimizing-arith-neg/src/Main.java b/test/411-optimizing-arith/src/NegTest.java index c53b639d40..83047269bb 100644 --- a/test/415-optimizing-arith-neg/src/Main.java +++ b/test/411-optimizing-arith/src/NegTest.java @@ -17,7 +17,7 @@  // Note that $opt$ is a marker for the optimizing compiler to test  // it does compile the method, and that $noinline$ is a marker to  // test that it does not inline it. -public class Main { +public class NegTest {    public static void assertEquals(int expected, int result) {      if (expected != result) { @@ -67,7 +67,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      negInt();      $opt$noinline$InplaceNegOneInt(1); @@ -169,55 +169,29 @@ public class Main {    } -  static boolean doThrow = false; -    private static void $opt$noinline$InplaceNegOneInt(int a) { -    if (doThrow) { -      // Try defeating inlining. -      throw new Error(); -    }      a = -a;      assertEquals(-1, a);    }    private static void $opt$noinline$InplaceNegOneLong(long a) { -    if (doThrow) { -      // Try defeating inlining. -      throw new Error(); -    }      a = -a;      assertEquals(-1L, a);    }    private static int $opt$noinline$NegInt(int a){ -    if (doThrow) { -      // Try defeating inlining. -      throw new Error(); -    }      return -a;    }    private static long $opt$noinline$NegLong(long a){ -    if (doThrow) { -      // Try defeating inlining. -      throw new Error(); -    }      return -a;    }    private static float $opt$noinline$NegFloat(float a){ -    if (doThrow) { -      // Try defeating inlining. -      throw new Error(); -    }      return -a;    }    private static double $opt$noinline$NegDouble(double a){ -    if (doThrow) { -      // Try defeating inlining. -      throw new Error(); -    }      return -a;    }  } diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/411-optimizing-arith/src/RemTest.java index 3f77318e6c..1b31f63569 100644 --- a/test/428-optimizing-arith-rem/src/Main.java +++ b/test/411-optimizing-arith/src/RemTest.java @@ -14,9 +14,9 @@   * limitations under the License.   */ -public class Main { +public class RemTest { -  public static void main(String[] args) { +  public static void main() {      remInt();      remLong();    } diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/411-optimizing-arith/src/ShiftsTest.java index b7a112f6a3..139ff70bf0 100644 --- a/test/431-optimizing-arith-shifts/src/Main.java +++ b/test/411-optimizing-arith/src/ShiftsTest.java @@ -14,7 +14,7 @@   * limitations under the License.   */ -public class Main { +public class ShiftsTest {    public static void expectEquals(int expected, int result) {      if (expected != result) { @@ -28,7 +28,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      testShlInt();      testShlLong();      testShrInt(); diff --git a/test/414-optimizing-arith-sub/src/Main.java b/test/411-optimizing-arith/src/SubTest.java index b4531cdfd4..9c9ea92f20 100644 --- a/test/414-optimizing-arith-sub/src/Main.java +++ b/test/411-optimizing-arith/src/SubTest.java @@ -16,7 +16,7 @@  // Note that $opt$ is a marker for the optimizing compiler to test  // it does compile the method. -public class Main { +public class SubTest {    public static void expectEquals(int expected, int result) {      if (expected != result) { @@ -70,7 +70,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      subInt();      subLong();      subFloat(); diff --git a/test/414-optimizing-arith-sub/expected.txt b/test/414-optimizing-arith-sub/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/test/414-optimizing-arith-sub/expected.txt +++ /dev/null diff --git a/test/414-optimizing-arith-sub/info.txt b/test/414-optimizing-arith-sub/info.txt deleted file mode 100644 index 1eaa14887b..0000000000 --- a/test/414-optimizing-arith-sub/info.txt +++ /dev/null @@ -1 +0,0 @@ -Subtraction tests. diff --git a/test/415-optimizing-arith-neg/expected.txt b/test/415-optimizing-arith-neg/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/test/415-optimizing-arith-neg/expected.txt +++ /dev/null diff --git a/test/415-optimizing-arith-neg/info.txt b/test/415-optimizing-arith-neg/info.txt deleted file mode 100644 index 8494aad938..0000000000 --- a/test/415-optimizing-arith-neg/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for arithmetic negation operations. diff --git a/test/417-optimizing-arith-div/expected.txt b/test/417-optimizing-arith-div/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/test/417-optimizing-arith-div/expected.txt +++ /dev/null diff --git a/test/417-optimizing-arith-div/info.txt b/test/417-optimizing-arith-div/info.txt deleted file mode 100644 index 1374b0ffb3..0000000000 --- a/test/417-optimizing-arith-div/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for division operation. diff --git a/test/428-optimizing-arith-rem/expected.txt b/test/428-optimizing-arith-rem/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/test/428-optimizing-arith-rem/expected.txt +++ /dev/null diff --git a/test/428-optimizing-arith-rem/info.txt b/test/428-optimizing-arith-rem/info.txt deleted file mode 100644 index 3e37ffeee8..0000000000 --- a/test/428-optimizing-arith-rem/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for modulo (rem) operation. diff --git a/test/431-optimizing-arith-shifts/expected.txt b/test/431-optimizing-arith-shifts/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/test/431-optimizing-arith-shifts/expected.txt +++ /dev/null diff --git a/test/431-optimizing-arith-shifts/info.txt b/test/431-optimizing-arith-shifts/info.txt deleted file mode 100644 index 14ff264662..0000000000 --- a/test/431-optimizing-arith-shifts/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for shift operations. diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java index 85c2fcaf22..c09da8125b 100644 --- a/test/646-checker-hadd-short/src/Main.java +++ b/test/646-checker-hadd-short/src/Main.java @@ -26,6 +26,10 @@ public class Main {    static short[] sB2 = new short[M];    static short[] sBo = new short[M]; +  private static int $inline$mone() { +    return -1; +  } +    /// CHECK-START: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (before)    /// CHECK-DAG: <<I1:i\d+>>   IntConstant 1                       loop:none    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none @@ -184,6 +188,35 @@ public class Main {      }    } +  /// CHECK-START: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (before) +  /// CHECK-DAG: <<I1:i\d+>>   IntConstant 1                       loop:none +  /// CHECK-DAG: <<M1:i\d+>>   IntConstant -1                      loop:none +  /// CHECK-DAG: <<I9:i\d+>>   IntConstant 9                       loop:none +  /// CHECK-DAG: <<M9:i\d+>>   IntConstant -9                      loop:none +  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<I9>>]               loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Add2:i\d+>> Add [<<Get2>>,<<M9>>]               loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Add2>>]             loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Sub:i\d+>>  Sub [<<Add3>>,<<M1>>]               loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Shr:i\d+>>  Shr [<<Sub>>,<<I1>>]                loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Shr>>]            loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none +  // +  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (after) +  /// CHECK-DAG: <<Get1:d\d+>> VecLoad                               loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Get2:d\d+>> VecLoad                               loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>>      outer_loop:none +  private static void rounding_halving_add_signed_alt3(short[] b1, short[] b2, short[] bo) { +    int min_length = Math.min(bo.length, Math.min(b1.length, b2.length)); +    for (int i = 0; i < min_length; i++) { +      // Computations that cancel to adding 1 also do not confuse recognition. +      bo[i] = (short) (((b1[i] + 9) + (b2[i] - 9) - $inline$mone()) >> 1); +    } +  } +    /// CHECK-START: void Main.rounding_halving_add_unsigned(short[], short[], short[]) instruction_simplifier (before)    /// CHECK-DAG: <<I1:i\d+>>   IntConstant 1                       loop:none    /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535                   loop:none @@ -366,6 +399,11 @@ public class Main {        short e = (short) ((sB1[i] + sB2[i] + 1) >> 1);        expectEquals(e, sBo[i]);      } +    rounding_halving_add_signed_alt3(sB1, sB2, sBo); +    for (int i = 0; i < M; i++) { +      short e = (short) ((sB1[i] + sB2[i] + 1) >> 1); +      expectEquals(e, sBo[i]); +    }      rounding_halving_add_unsigned(sB1, sB2, sBo);      for (int i = 0; i < M; i++) {        short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff) + 1) >> 1); diff --git a/test/651-checker-byte-simd-minmax/expected.txt b/test/651-checker-byte-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-byte-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-char-simd-minmax/expected.txt b/test/651-checker-char-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-char-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-char-simd-minmax/info.txt b/test/651-checker-char-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-char-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-double-simd-minmax/expected.txt b/test/651-checker-double-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-double-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-double-simd-minmax/info.txt b/test/651-checker-double-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-double-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-float-simd-minmax/expected.txt b/test/651-checker-float-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-float-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-float-simd-minmax/info.txt b/test/651-checker-float-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-float-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-int-simd-minmax/expected.txt b/test/651-checker-int-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-int-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-int-simd-minmax/info.txt b/test/651-checker-int-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-int-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-long-simd-minmax/expected.txt b/test/651-checker-long-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-long-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-long-simd-minmax/info.txt b/test/651-checker-long-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-long-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-short-simd-minmax/expected.txt b/test/651-checker-short-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-short-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-short-simd-minmax/info.txt b/test/651-checker-short-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-short-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-simd-minmax/expected.txt b/test/651-checker-simd-minmax/expected.txt new file mode 100644 index 0000000000..f362c45b77 --- /dev/null +++ b/test/651-checker-simd-minmax/expected.txt @@ -0,0 +1,7 @@ +ByteSimdMinMax passed +CharSimdMinMax passed +ShortSimdMinMax passed +IntSimdMinMax passed +LongSimdMinMax passed +DoubleSimdMinMax passed +FloatSimdMinMax passed diff --git a/test/651-checker-byte-simd-minmax/info.txt b/test/651-checker-simd-minmax/info.txt index 73af1242c0..73af1242c0 100644 --- a/test/651-checker-byte-simd-minmax/info.txt +++ b/test/651-checker-simd-minmax/info.txt diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java index 4e667bbc6f..b9954947f7 100644 --- a/test/651-checker-byte-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java @@ -17,9 +17,9 @@  /**   * Tests for MIN/MAX vectorization.   */ -public class Main { +public class ByteSimdMinMax { -  /// CHECK-START: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (before) +  /// CHECK-START: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -27,7 +27,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none @@ -39,7 +39,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMinUnsigned(byte[], byte[], byte[]) instruction_simplifier (before) +  /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) instruction_simplifier (before)    /// CHECK-DAG: <<I255:i\d+>> IntConstant 255                     loop:none    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -50,7 +50,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (before) +  /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -58,7 +58,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none @@ -70,7 +70,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (before) +  /// CHECK-START: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -78,7 +78,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none @@ -90,7 +90,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMaxUnsigned(byte[], byte[], byte[]) instruction_simplifier (before) +  /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) instruction_simplifier (before)    /// CHECK-DAG: <<I255:i\d+>> IntConstant 255                     loop:none    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:b\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -101,7 +101,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (before) +  /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:a\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -109,7 +109,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>      outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none @@ -121,7 +121,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMin100(byte[], byte[]) loop_optimization (before) +  /// CHECK-START: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (before)    /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get:b\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -129,7 +129,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:b\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(byte[], byte[]) loop_optimization (after) +  /// CHECK-START-{ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after)    /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none    /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none    /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>  outer_loop:none @@ -142,7 +142,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      // Initialize cross-values for all possible values.      int total = 256 * 256;      byte[] x = new byte[total]; @@ -185,7 +185,7 @@ public class Main {        expectEquals(expected, x[i]);      } -    System.out.println("passed"); +    System.out.println("ByteSimdMinMax passed");    }    private static void expectEquals(byte expected, byte result) { diff --git a/test/651-checker-char-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/CharSimdMinMax.java index 520e10b6c1..30169c4591 100644 --- a/test/651-checker-char-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/CharSimdMinMax.java @@ -17,9 +17,9 @@  /**   * Tests for MIN/MAX vectorization.   */ -public class Main { +public class CharSimdMinMax { -  /// CHECK-START: void Main.doitMin(char[], char[], char[]) loop_optimization (before) +  /// CHECK-START: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -27,7 +27,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(char[], char[], char[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none @@ -39,7 +39,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMax(char[], char[], char[]) loop_optimization (before) +  /// CHECK-START: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -47,7 +47,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(char[], char[], char[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none @@ -59,7 +59,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMin100(char[], char[]) loop_optimization (before) +  /// CHECK-START: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (before)    /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get:c\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -67,7 +67,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:c\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(char[], char[]) loop_optimization (after) +  /// CHECK-START-{ARM64,MIPS64}: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (after)    /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none    /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none    /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>    outer_loop:none @@ -80,7 +80,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      char[] interesting = {        0x0000, 0x0001, 0x007f, 0x0080, 0x0081, 0x00ff,        0x0100, 0x0101, 0x017f, 0x0180, 0x0181, 0x01ff, @@ -121,7 +121,7 @@ public class Main {        expectEquals(expected, x[i]);      } -    System.out.println("passed"); +    System.out.println("CharSimdMinMax passed");    }    private static void expectEquals(char expected, char result) { diff --git a/test/651-checker-double-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java index 2eaf907167..da20594db8 100644 --- a/test/651-checker-double-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java @@ -17,9 +17,9 @@  /**   * Tests for MIN/MAX vectorization.   */ -public class Main { +public class DoubleSimdMinMax { -  /// CHECK-START: void Main.doitMin(double[], double[], double[]) loop_optimization (before) +  /// CHECK-START: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -29,7 +29,7 @@ public class Main {    // TODO x86: 0.0 vs -0.0?    // TODO MIPS64: min(x, NaN)?    // -  /// CHECK-START-ARM64: void Main.doitMin(double[], double[], double[]) loop_optimization (after) +  /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none @@ -41,7 +41,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMax(double[], double[], double[]) loop_optimization (before) +  /// CHECK-START: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -51,7 +51,7 @@ public class Main {    // TODO x86: 0.0 vs -0.0?    // TODO MIPS64: max(x, NaN)?    // -  /// CHECK-START-ARM64: void Main.doitMax(double[], double[], double[]) loop_optimization (after) +  /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none @@ -63,7 +63,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      double[] interesting = {        -0.0f,        +0.0f, @@ -109,7 +109,7 @@ public class Main {        expectEquals(expected, x[i]);      } -    System.out.println("passed"); +    System.out.println("DoubleSimdMinMax passed");    }    private static void expectEquals(double expected, double result) { diff --git a/test/651-checker-float-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/FloatSimdMinMax.java index dc09dfc7cc..645081248a 100644 --- a/test/651-checker-float-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/FloatSimdMinMax.java @@ -17,9 +17,9 @@  /**   * Tests for MIN/MAX vectorization.   */ -public class Main { +public class FloatSimdMinMax { -  /// CHECK-START: void Main.doitMin(float[], float[], float[]) loop_optimization (before) +  /// CHECK-START: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -29,7 +29,7 @@ public class Main {    // TODO x86: 0.0 vs -0.0?    // TODO MIPS64: min(x, NaN)?    // -  /// CHECK-START-ARM64: void Main.doitMin(float[], float[], float[]) loop_optimization (after) +  /// CHECK-START-ARM64: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none @@ -41,7 +41,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMax(float[], float[], float[]) loop_optimization (before) +  /// CHECK-START: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:f\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -51,7 +51,7 @@ public class Main {    // TODO x86: 0.0 vs -0.0?    // TODO MIPS64: max(x, NaN)?    // -  /// CHECK-START-ARM64: void Main.doitMax(float[], float[], float[]) loop_optimization (after) +  /// CHECK-START-ARM64: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none @@ -63,7 +63,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      float[] interesting = {        -0.0f,        +0.0f, @@ -109,7 +109,7 @@ public class Main {        expectEquals(expected, x[i]);      } -    System.out.println("passed"); +    System.out.println("FloatSimdMinMax passed");    }    private static void expectEquals(float expected, float result) { diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/IntSimdMinMax.java index 82fad84d08..6373ae10eb 100644 --- a/test/651-checker-int-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/IntSimdMinMax.java @@ -17,16 +17,16 @@  /**   * Tests for MIN/MAX vectorization.   */ -public class Main { +public class IntSimdMinMax { -  /// CHECK-START: void Main.doitMin(int[], int[], int[]) loop_optimization (before) +  /// CHECK-START: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Min:i\d+>>  Min [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(int[], int[], int[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                                      loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                                      loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Int32 loop:<<Loop>>      outer_loop:none @@ -38,14 +38,14 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMax(int[], int[], int[]) loop_optimization (before) +  /// CHECK-START: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:i\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Max:i\d+>>  Max [<<Get1>>,<<Get2>>]             loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(int[], int[], int[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                                      loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                                      loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Int32 loop:<<Loop>>      outer_loop:none @@ -57,7 +57,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      int[] interesting = {        0x00000000, 0x00000001, 0x00007fff, 0x00008000, 0x00008001, 0x0000ffff,        0x00010000, 0x00010001, 0x00017fff, 0x00018000, 0x00018001, 0x0001ffff, @@ -93,7 +93,7 @@ public class Main {        expectEquals(expected, x[i]);      } -    System.out.println("passed"); +    System.out.println("IntSimdMinMax passed");    }    private static void expectEquals(int expected, int result) { diff --git a/test/651-checker-long-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/LongSimdMinMax.java index f52686e54c..bb0c6047ed 100644 --- a/test/651-checker-long-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/LongSimdMinMax.java @@ -17,9 +17,9 @@  /**   * Tests for MIN/MAX vectorization.   */ -public class Main { +public class LongSimdMinMax { -  /// CHECK-START: void Main.doitMin(long[], long[], long[]) loop_optimization (before) +  /// CHECK-START: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -28,10 +28,10 @@ public class Main {    //    // Not directly supported for longs.    // -  /// CHECK-START-ARM64: void Main.doitMin(long[], long[], long[]) loop_optimization (after) +  /// CHECK-START-ARM64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after)    /// CHECK-NOT: VecMin    // -  /// CHECK-START-MIPS64: void Main.doitMin(long[], long[], long[]) loop_optimization (after) +  /// CHECK-START-MIPS64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none @@ -44,7 +44,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMax(long[], long[], long[]) loop_optimization (before) +  /// CHECK-START: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:j\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -53,10 +53,10 @@ public class Main {    //    // Not directly supported for longs.    // -  /// CHECK-START-ARM64: void Main.doitMax(long[], long[], long[]) loop_optimization (after) +  /// CHECK-START-ARM64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after)    /// CHECK-NOT: VecMax    // -  /// CHECK-START-MIPS64: void Main.doitMax(long[], long[], long[]) loop_optimization (after) +  /// CHECK-START-MIPS64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>]           loop:<<Loop>>      outer_loop:none @@ -68,7 +68,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      long[] interesting = {        0x0000000000000000L, 0x0000000000000001L, 0x000000007fffffffL,        0x0000000080000000L, 0x0000000080000001L, 0x00000000ffffffffL, @@ -110,7 +110,7 @@ public class Main {        expectEquals(expected, x[i]);      } -    System.out.println("passed"); +    System.out.println("LongSimdMinMax passed");    }    private static void expectEquals(long expected, long result) { diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/Main.java new file mode 100644 index 0000000000..9134dd1edd --- /dev/null +++ b/test/651-checker-simd-minmax/src/Main.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { +  public static void main(String[] args) { +    ByteSimdMinMax.main(); +    CharSimdMinMax.main(); +    ShortSimdMinMax.main(); +    IntSimdMinMax.main(); +    LongSimdMinMax.main(); +    DoubleSimdMinMax.main(); +    FloatSimdMinMax.main(); +  } +} diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java index 4300ca2951..aae78914d8 100644 --- a/test/651-checker-short-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java @@ -17,9 +17,9 @@  /**   * Tests for MIN/MAX vectorization.   */ -public class Main { +public class ShortSimdMinMax { -  /// CHECK-START: void Main.doitMin(short[], short[], short[]) loop_optimization (before) +  /// CHECK-START: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -27,7 +27,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(short[], short[], short[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>    outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>         outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none @@ -39,7 +39,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMinUnsigned(short[], short[], short[]) instruction_simplifier (before) +  /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) instruction_simplifier (before)    /// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535                   loop:none    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -50,7 +50,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (before) +  /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -58,7 +58,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>     outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>          outer_loop:none    /// CHECK-DAG: <<Min:d\d+>>  VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none @@ -70,7 +70,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMax(short[], short[], short[]) loop_optimization (before) +  /// CHECK-START: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -78,7 +78,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(short[], short[], short[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>    outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>         outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none @@ -90,7 +90,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMaxUnsigned(short[], short[], short[]) instruction_simplifier (before) +  /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) instruction_simplifier (before)    /// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535                   loop:none    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:s\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -101,7 +101,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (before) +  /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (before)    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get1:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG: <<Get2:c\d+>> ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -109,7 +109,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Max>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) +  /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecLoad                              loop:<<Loop:B\d+>>     outer_loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad                              loop:<<Loop>>          outer_loop:none    /// CHECK-DAG: <<Max:d\d+>>  VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none @@ -121,7 +121,7 @@ public class Main {      }    } -  /// CHECK-START: void Main.doitMin100(short[], short[]) loop_optimization (before) +  /// CHECK-START: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (before)    /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                     loop:none    /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none    /// CHECK-DAG: <<Get:s\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none @@ -129,7 +129,7 @@ public class Main {    /// CHECK-DAG: <<Cnv:s\d+>>  TypeConversion [<<Min>>]            loop:<<Loop>>      outer_loop:none    /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none    // -  /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(short[], short[]) loop_optimization (after) +  /// CHECK-START-{ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after)    /// CHECK-DAG: <<I100:i\d+>> IntConstant 100                      loop:none    /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>]        loop:none    /// CHECK-DAG: <<Get:d\d+>>  VecLoad                              loop:<<Loop:B\d+>>   outer_loop:none @@ -142,7 +142,7 @@ public class Main {      }    } -  public static void main(String[] args) { +  public static void main() {      short[] interesting = {        (short) 0x0000, (short) 0x0001, (short) 0x007f,        (short) 0x0080, (short) 0x0081, (short) 0x00ff, @@ -199,7 +199,7 @@ public class Main {        expectEquals(expected, x[i]);      } -    System.out.println("passed"); +    System.out.println("ShortSimdMinMax passed");    }    private static void expectEquals(short expected, short result) { diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java index 8a44d9ed12..77c9e53e0c 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -19,6 +19,10 @@   */  public class Main { +  private static int $inline$seven() { +    return 7; +  } +    // TODO: lower precision still coming, b/64091002    private static short sadShort2Short(short[] s1, short[] s2) { @@ -153,6 +157,102 @@ public class Main {      return sad;    } +  /// CHECK-START: int Main.sadShort2IntConstant1(short[]) loop_optimization (before) +  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none +  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none +  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant -7                 loop:none +  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Get1:s\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Add:i\d+>>    Add [<<Get1>>,<<Cons>>]        loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Add>>]                  loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi2>>,<<Intrin>>]      loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]       loop:<<Loop>>      outer_loop:none +  // +  /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant1(short[]) loop_optimization (after) +  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none +  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none +  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant 7                  loop:none +  /// CHECK-DAG: <<Cons8:i\d+>>  IntConstant 8                  loop:none +  /// CHECK-DAG: <<Rep:d\d+>>    VecReplicateScalar [<<Cons>>]  loop:none +  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Cons0>>]      loop:none +  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]         loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Load1:d\d+>>  VecLoad [{{l\d+}},<<Phi1>>]    loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<SAD:d\d+>>    VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons8>>]       loop:<<Loop>>      outer_loop:none +  private static int sadShort2IntConstant1(short[] s) { +    int sad = 0; +    for (int i = 0; i < s.length; i++) { +      sad += Math.abs(s[i] - 7);  // s[i] + -7 +    } +    return sad; +  } + +  /// CHECK-START: int Main.sadShort2IntConstant2(short[]) loop_optimization (before) +  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none +  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none +  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant 7                  loop:none +  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Get1:s\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Sub:i\d+>>    Sub [<<Get1>>,<<Cons>>]        loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Sub>>]                  loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi2>>,<<Intrin>>]      loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]       loop:<<Loop>>      outer_loop:none +  // +  /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant2(short[]) loop_optimization (after) +  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none +  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none +  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant 7                  loop:none +  /// CHECK-DAG: <<Cons8:i\d+>>  IntConstant 8                  loop:none +  /// CHECK-DAG: <<Rep:d\d+>>    VecReplicateScalar [<<Cons>>]  loop:none +  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Cons0>>]      loop:none +  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]         loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Load1:d\d+>>  VecLoad [{{l\d+}},<<Phi1>>]    loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<SAD:d\d+>>    VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons8>>]       loop:<<Loop>>      outer_loop:none +  private static int sadShort2IntConstant2(short[] s) { +    int sad = 0; +    for (int i = 0; i < s.length; i++) { +      sad += Math.abs(s[i] - $inline$seven());  // s[i] - 7 +    } +    return sad; +  } + +  /// CHECK-START: int Main.sadShort2IntConstant3(short[]) loop_optimization (before) +  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none +  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none +  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant 7                  loop:none +  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Get1:s\d+>>   ArrayGet [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Add:i\d+>>    Add [<<Get1>>,<<Cons>>]        loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Add>>]                  loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi2>>,<<Intrin>>]      loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]       loop:<<Loop>>      outer_loop:none +  // +  /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant3(short[]) loop_optimization (after) +  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none +  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none +  /// CHECK-DAG: <<Cons:i\d+>>   IntConstant -7                 loop:none +  /// CHECK-DAG: <<Cons8:i\d+>>  IntConstant 8                  loop:none +  /// CHECK-DAG: <<Rep:d\d+>>    VecReplicateScalar [<<Cons>>]  loop:none +  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Cons0>>]      loop:none +  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]       loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]         loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<Load1:d\d+>>  VecLoad [{{l\d+}},<<Phi1>>]    loop:<<Loop>>      outer_loop:none +  /// CHECK-DAG: <<SAD:d\d+>>    VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons8>>]       loop:<<Loop>>      outer_loop:none +  private static int sadShort2IntConstant3(short[] s) { +    int sad = 0; +    for (int i = 0; i < s.length; i++) { +      sad += Math.abs(s[i] + $inline$seven());  // hidden s[i] - (-7) +    } +    return sad; +  } +    /// CHECK-START: long Main.sadShort2Long(short[], short[]) loop_optimization (before)    /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                  loop:none    /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                  loop:none @@ -243,6 +343,9 @@ public class Main {      expectEquals(65535, sadShort2IntAlt(s2, s1));      expectEquals(65535, sadShort2IntAlt2(s1, s2));      expectEquals(65535, sadShort2IntAlt2(s2, s1)); +    expectEquals(32880, sadShort2IntConstant1(s1)); +    expectEquals(32880, sadShort2IntConstant2(s1)); +    expectEquals(32866, sadShort2IntConstant3(s1));      expectEquals(65535L, sadShort2Long(s1, s2));      expectEquals(65535L, sadShort2Long(s2, s1));      expectEquals(65536L, sadShort2LongAt1(s1, s2)); @@ -279,6 +382,9 @@ public class Main {      expectEquals(1291788, sadShort2Int(s1, s2));      expectEquals(1291788, sadShort2IntAlt(s1, s2));      expectEquals(1291788, sadShort2IntAlt2(s1, s2)); +    expectEquals(823907, sadShort2IntConstant1(s1)); +    expectEquals(823907, sadShort2IntConstant2(s1)); +    expectEquals(823953, sadShort2IntConstant3(s1));      expectEquals(1291788L, sadShort2Long(s1, s2));      expectEquals(1291789L, sadShort2LongAt1(s1, s2)); diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java index d123cc2e25..decc691789 100644 --- a/test/678-checker-simd-saturation/src/Main.java +++ b/test/678-checker-simd-saturation/src/Main.java @@ -19,6 +19,14 @@   */  public class Main { +  static final int $inline$p15() { +    return 15; +  } + +  static final int $inline$m15() { +    return -15; +  } +    //    // Direct min-max.    // @@ -230,8 +238,8 @@ public class Main {    /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none -  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none -  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none +  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none    public static void satSubPConstSByte(byte[] a, byte[] b) {      int n = Math.min(a.length, b.length);      for (int i = 0; i < n; i++) { @@ -242,8 +250,8 @@ public class Main {    /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none -  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none -  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none +  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none    public static void satSubNConstSByte(byte[] a, byte[] b) {      int n = Math.min(a.length, b.length);      for (int i = 0; i < n; i++) { @@ -282,8 +290,8 @@ public class Main {    /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none -  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none -  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none +  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none    public static void satSubPConstSShort(short[] a, short[] b) {      int n = Math.min(a.length, b.length);      for (int i = 0; i < n; i++) { @@ -294,8 +302,8 @@ public class Main {    /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after)    /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none    /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none -  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none -  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none +  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none    public static void satSubNConstSShort(short[] a, short[] b) {      int n = Math.min(a.length, b.length);      for (int i = 0; i < n; i++) { @@ -304,7 +312,59 @@ public class Main {    }    // -  // Alternatives. +  // Alternatives 8-bit clipping. +  // + +  /// CHECK-START-{ARM,ARM64}: void Main.usatAddConst(byte[], byte[]) loop_optimization (after) +  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none +  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none +  public static void usatAddConst(byte[] a, byte[] b) { +    int n = Math.min(a.length, b.length); +    for (int i = 0; i < n; i++) { +      b[i] = (byte) Math.min((a[i] & 0xff) + $inline$p15(), 255); +    } +  } + +  /// CHECK-START-{ARM,ARM64}: void Main.usatAddConstAlt(byte[], byte[]) loop_optimization (after) +  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none +  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Add:d\d+>>  VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Add>>]  loop:<<Loop>> outer_loop:none +  public static void usatAddConstAlt(byte[] a, byte[] b) { +    int n = Math.min(a.length, b.length); +    for (int i = 0; i < n; i++) { +      b[i] = (byte) Math.min((a[i] & 0xff) - $inline$m15(), 255); +    } +  } + +  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(byte[], byte[]) loop_optimization (after) +  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none +  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none +  public static void usatSubConst(byte[] a, byte[] b) { +    int n = Math.min(a.length, b.length); +    for (int i = 0; i < n; i++) { +      b[i] = (byte) Math.max((a[i] & 0xff) - $inline$p15(), 0); +    } +  } + +  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(byte[], byte[]) loop_optimization (after) +  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none +  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none +  public static void usatSubConstAlt(byte[] a, byte[] b) { +    int n = Math.min(a.length, b.length); +    for (int i = 0; i < n; i++) { +      b[i] = (byte) Math.max((a[i] & 0xff) + $inline$m15(), 0); +    } +  } + +  // +  // Alternatives 16-bit clipping.    //    /// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before) @@ -442,6 +502,34 @@ public class Main {      }    } +  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(short[], short[]) loop_optimization (after) +  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none +  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none +  public static void usatSubConst(short[] a, short[] b) { +    int n = Math.min(a.length, b.length); +    for (int i = 0; i < n; i++) { +      int t = a[i] & 0xffff; +      int s = t - $inline$p15(); +      b[i] = (short)(s > 0 ? s : 0); +    } +  } + +  /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(short[], short[]) loop_optimization (after) +  /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar                   loop:none +  /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>]      loop:<<Loop:B\d+>> outer_loop:none +  /// CHECK-DAG: <<Sub:d\d+>>  VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none +  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Sub>>]  loop:<<Loop>> outer_loop:none +  public static void usatSubConstAlt(short[] a, short[] b) { +    int n = Math.min(a.length, b.length); +    for (int i = 0; i < n; i++) { +      int t = a[i] & 0xffff; +      int s = t + $inline$m15(); +      b[i] = (short)(s > 0 ? s : 0); +    } +  } +    //    // Test drivers.    // @@ -503,6 +591,27 @@ public class Main {        byte e = (byte) Math.max(-15 - b1[i], -128);        expectEquals(e, out[i]);      } +    // Alternatives. +    usatAddConst(b1, out); +    for (int i = 0; i < m; i++) { +      byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); +      expectEquals(e, out[i]); +    } +    usatAddConstAlt(b1, out); +    for (int i = 0; i < m; i++) { +      byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); +      expectEquals(e, out[i]); +    } +    usatSubConst(b1, out); +    for (int i = 0; i < m; i++) { +      byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); +      expectEquals(e, out[i]); +    } +    usatSubConstAlt(b1, out); +    for (int i = 0; i < m; i++) { +      byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); +      expectEquals(e, out[i]); +    }    }    private static void test16Bit() { @@ -630,6 +739,16 @@ public class Main {        short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752);        expectEquals(e, out[i]);      } +    usatSubConst(s1, out); +    for (int i = 0; i < m; i++) { +      short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); +      expectEquals(e, out[i]); +    } +    usatSubConstAlt(s1, out); +    for (int i = 0; i < m; i++) { +      short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); +      expectEquals(e, out[i]); +    }    }    public static void main(String[] args) { diff --git a/test/680-checker-deopt-dex-pc-0/expected.txt b/test/680-checker-deopt-dex-pc-0/expected.txt new file mode 100644 index 0000000000..805857dc65 --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +passed diff --git a/test/680-checker-deopt-dex-pc-0/info.txt b/test/680-checker-deopt-dex-pc-0/info.txt new file mode 100644 index 0000000000..8eae156b22 --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/info.txt @@ -0,0 +1,2 @@ +Regression test for deoptimization at dex pc 0 causing infinite recursion +for JIT-at-first-use. diff --git a/test/680-checker-deopt-dex-pc-0/src/Main.java b/test/680-checker-deopt-dex-pc-0/src/Main.java new file mode 100644 index 0000000000..64a3cb3da7 --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/src/Main.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { +    // We run this test for AOT to verify that there is a HDeoptimize with dex pc 0. +    /// CHECK-START: int Main.$noinline$getInt(byte[], int) BCE (after) +    /// CHECK:          Deoptimize dex_pc:0 +    public static int $noinline$getInt(byte[] array, int offset) { +        // The aget for `array[offset]` is at dex pc 0, so the Deoptimize +        // from dynamic BCE shall also be at dex pc 0. +        return ((array[offset    ] & 0xFF) <<  0) + +               ((array[offset + 1] & 0xFF) <<  8) + +               ((array[offset + 2] & 0xFF) << 16) + +               ((array[offset + 3] & 0xFF) << 24); +    } + +    public static void main(String[] args) { +        System.loadLibrary(args[0]); +        if (hasJit()) { +            byte[] array = { 0, 1, 2, 3 }; +            ensureJitCompiled(Main.class, "$noinline$getInt"); +            if (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) { +                throw new Error("Unexpected entrypoint!"); +            } +            if ($noinline$getInt(array, 0) != 0x03020100) { +                throw new Error(); +            } +            try { +                // The HDeoptimize at dex pc 0 was previously handled poorly as the dex pc 0 +                // was used to detect whether we entered the method. This meant that the +                // instrumentation would have reported MethodEnteredEvent and we would have +                // told JIT that the method was entered. With JIT-on-first-use we would also +                // immediatelly recompile the method and run the compiled code leading to +                // a an infinite deoptimization recursion, yielding StackOverflowError. +                $noinline$getInt(array, 1); +            } catch (ArrayIndexOutOfBoundsException ignored) {} +        } +        System.out.println("passed"); +    } + +    public static native boolean hasJit(); +    public native static boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName); +    public native static void ensureJitCompiled(Class<?> cls, String methodName); +} diff --git a/test/HiddenApiSignatures/Class1.java b/test/HiddenApiSignatures/Class1.java new file mode 100644 index 0000000000..a9004dcd99 --- /dev/null +++ b/test/HiddenApiSignatures/Class1.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mypackage.packagea; + +public class Class1 { +    public int field1; +    public int field12; + +    public Class1() { +    } + +    public void method1() { +    } + +    public void method1(int i) { +    } + +    public void method12() { +    } + +}
\ No newline at end of file diff --git a/test/HiddenApiSignatures/Class12.java b/test/HiddenApiSignatures/Class12.java new file mode 100644 index 0000000000..82b22e365f --- /dev/null +++ b/test/HiddenApiSignatures/Class12.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mypackage.packagea; + +public class Class12 { +    public int field1; + +    public void method1() { +    } +}
\ No newline at end of file diff --git a/test/HiddenApiSignatures/Class2.java b/test/HiddenApiSignatures/Class2.java new file mode 100644 index 0000000000..dc92b9cfd8 --- /dev/null +++ b/test/HiddenApiSignatures/Class2.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mypackage.packagea; + +public class Class2 { +    public int field1; + +    public void method1() { +    } + +    public void method1(int i) { +    } +}
\ No newline at end of file diff --git a/test/HiddenApiSignatures/Class3.java b/test/HiddenApiSignatures/Class3.java new file mode 100644 index 0000000000..fbf04071a4 --- /dev/null +++ b/test/HiddenApiSignatures/Class3.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mypackage.packageb; + +public class Class3 { +    public int field1; + +    public void method1() { +    } + +    public void method1(int i) { +    } +}
\ No newline at end of file diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index e9127a8101..86adb733a9 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -775,9 +775,6 @@ if [ "$HOST" = "n" ]; then    TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp"  fi -# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind. -# b/27185632 -# b/24664297  dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \                    $GDB_ARGS \                    $FLAGS \ @@ -792,7 +789,6 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \                    $DEBUGGER_OPTS \                    $DALVIKVM_BOOT_OPT \                    $TMP_DIR_OPTION \ -                  -XX:DumpNativeStackOnSigQuit:false \                    -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS"  # Remove whitespace. diff --git a/test/knownfailures.json b/test/knownfailures.json index d390c4c049..6d8abe13b2 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -976,5 +976,11 @@          "tests": "677-fsi",          "variant": "no-dex2oat | no-image | no-prebuild | relocate-npatchoat | jvm",          "description": ["Test requires a successful dex2oat invocation"] +    }, +    { +        "tests": ["990-field-trace", +                  "991-field-trace-2"], +        "variant": "gcstress & debug & target", +        "description": ["Test can time out on gcstress with debug"]      }  ] diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index b323ddc9cb..95e488dc4f 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -46,6 +46,10 @@ target_config = {      'art-jit' : {          'run-test' : ['--jit']      }, +    'art-jit-on-first-use' : { +        'run-test' : ['--jit', +                      '--runtime-option=-Xjitthreshold:0'] +    },      'art-pictest' : {          'run-test' : ['--pictest',                        '--optimizing'] @@ -66,6 +70,11 @@ target_config = {          'run-test' : ['--jit',                        '--gcstress']      }, +    'art-jit-on-first-use-gcstress' : { +        'run-test' : ['--jit', +                      '--gcstress', +                      '--runtime-option=-Xjitthreshold:0'] +    },      # TODO: Rename or repurpose this configuration as      # 'art-read-barrier-heap-poisoning' (b/62611253).      'art-read-barrier' : { diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index 31ff682828..cbf62d9e9c 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -12,10 +12,12 @@  // See the License for the specific language governing permissions and  // limitations under the License. -art_cc_binary { +cc_binary {      name: "veridex",      host_supported: true,      srcs: [ +        "hidden_api.cc", +        "hidden_api_finder.cc",          "resolver.cc",          "veridex.cc",      ], diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk new file mode 100644 index 0000000000..4183054193 --- /dev/null +++ b/tools/veridex/Android.mk @@ -0,0 +1,35 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +#      http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex +$(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 +$(system_stub_dex): $(TOPDIR)prebuilts/sdk/system_current/android.jar | $(ZIP2ZIP) $(DX) +	$(transform-classes-d8.jar-to-dex) + + +oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex +$(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 +$(oahl_stub_dex): $(TOPDIR)prebuilts/sdk/org.apache.http.legacy/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX) +	$(transform-classes-d8.jar-to-dex) + +.PHONY: appcompat + +appcompat: $(system_stub_dex) $(oahl_stub_dex) $(HOST_OUT_EXECUTABLES)/veridex \ +  ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-light-greylist.txt \ +  ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-dark-greylist.txt \ +  ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-blacklist.txt diff --git a/tools/veridex/README.md b/tools/veridex/README.md new file mode 100644 index 0000000000..0f91b08771 --- /dev/null +++ b/tools/veridex/README.md @@ -0,0 +1,14 @@ +appcompat.sh +============ + +Given an APK, finds API uses that fall into the blacklist/greylists APIs. + +NOTE: appcompat.sh is still under development. It can report +API uses that do not execute at runtime, and reflection uses +that do not exist. It can also miss on reflection uses. + +To build it: +> make appcompat + +To run it: +> ./art/tools/veridex/appcompat.sh test.apk diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh new file mode 100755 index 0000000000..f75aa4f0d0 --- /dev/null +++ b/tools/veridex/appcompat.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +#      http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# We want to be at the root for simplifying the "out" detection +# logic. +if [ ! -d art ]; then +  echo "Script needs to be run at the root of the android tree." +  exit 1 +fi + +# Logic for setting out_dir from build/make/core/envsetup.mk: +if [[ -z $OUT_DIR ]]; then +  if [[ -z $OUT_DIR_COMMON_BASE ]]; then +    OUT=out +  else +    OUT=${OUT_DIR_COMMON_BASE}/${PWD##*/} +  fi +else +  OUT=${OUT_DIR} +fi + +PACKAGING=${OUT}/target/common/obj/PACKAGING + +if [ -z "$ANDROID_HOST_OUT" ] ; then +  ANDROID_HOST_OUT=${OUT}/host/linux-x86 +fi + +echo "NOTE: appcompat.sh is still under development. It can report" +echo "API uses that do not execute at runtime, and reflection uses" +echo "that do not exist. It can also miss on reflection uses." + + +${ANDROID_HOST_OUT}/bin/veridex \ +    --core-stubs=${PACKAGING}/core_dex_intermediates/classes.dex:${PACKAGING}/oahl_dex_intermediates/classes.dex \ +    --blacklist=${PACKAGING}/hiddenapi-blacklist.txt \ +    --light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \ +    --dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \ +    --dex-file=$1 diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc new file mode 100644 index 0000000000..93f921a25f --- /dev/null +++ b/tools/veridex/hidden_api.cc @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software +  * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hidden_api.h" + +#include <fstream> +#include <sstream> + +#include "dex/dex_file-inl.h" + +namespace art { + +std::string HiddenApi::GetApiMethodName(const DexFile& dex_file, uint32_t method_index) { +  std::stringstream ss; +  const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index); +  ss << dex_file.StringByTypeIdx(method_id.class_idx_) +     << "->" +     << dex_file.GetMethodName(method_id) +     << dex_file.GetMethodSignature(method_id).ToString(); +  return ss.str(); +} + +std::string HiddenApi::GetApiFieldName(const DexFile& dex_file, uint32_t field_index) { +  std::stringstream ss; +  const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); +  ss << dex_file.StringByTypeIdx(field_id.class_idx_) +     << "->" +     << dex_file.GetFieldName(field_id) +     << ":" +     << dex_file.GetFieldTypeDescriptor(field_id); +  return ss.str(); +} + +void HiddenApi::FillList(const char* filename, std::set<std::string>& entries) { +  if (filename == nullptr) { +    return; +  } +  std::ifstream in(filename); +  std::string str; +  while (std::getline(in, str)) { +    entries.insert(str); +    size_t pos = str.find("->"); +    if (pos != std::string::npos) { +      // Add the class name. +      entries.insert(str.substr(0, pos)); +      pos = str.find('('); +      if (pos != std::string::npos) { +        // Add the class->method name (so stripping the signature). +        entries.insert(str.substr(0, pos)); +      } +    } +  } +} + +}  // namespace art diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h new file mode 100644 index 0000000000..5893b8ae33 --- /dev/null +++ b/tools/veridex/hidden_api.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software +  * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_ +#define ART_TOOLS_VERIDEX_HIDDEN_API_H_ + +#include "dex/hidden_api_access_flags.h" + +#include <ostream> +#include <set> +#include <string> + +namespace art { + +class DexFile; + +/** + * Helper class for logging if a method/field is in a hidden API list. + */ +class HiddenApi { + public: +  HiddenApi(const char* blacklist, const char* dark_greylist, const char* light_greylist) { +    FillList(light_greylist, light_greylist_); +    FillList(dark_greylist, dark_greylist_); +    FillList(blacklist, blacklist_); +  } + +  HiddenApiAccessFlags::ApiList GetApiList(const std::string& name) const { +    if (IsInList(name, blacklist_)) { +      return HiddenApiAccessFlags::kBlacklist; +    } else if (IsInList(name, dark_greylist_)) { +      return HiddenApiAccessFlags::kDarkGreylist; +    } else if (IsInList(name, light_greylist_)) { +      return HiddenApiAccessFlags::kLightGreylist; +    } else { +      return HiddenApiAccessFlags::kWhitelist; +    } +  } + +  bool IsInRestrictionList(const std::string& name) const { +    return GetApiList(name) != HiddenApiAccessFlags::kWhitelist; +  } + +  static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index); + +  static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index); + + private: +  static bool IsInList(const std::string& name, const std::set<std::string>& list) { +    return list.find(name) != list.end(); +  } + +  static void FillList(const char* filename, std::set<std::string>& entries); + +  std::set<std::string> blacklist_; +  std::set<std::string> light_greylist_; +  std::set<std::string> dark_greylist_; +}; + +}  // namespace art + +#endif  // ART_TOOLS_VERIDEX_HIDDEN_API_H_ diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc new file mode 100644 index 0000000000..d611f78eed --- /dev/null +++ b/tools/veridex/hidden_api_finder.cc @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software +  * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hidden_api_finder.h" + +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" +#include "dex/dex_file.h" +#include "dex/method_reference.h" +#include "hidden_api.h" +#include "resolver.h" +#include "veridex.h" + +#include <iostream> + +namespace art { + +void HiddenApiFinder::CheckMethod(uint32_t method_id, +                                  VeridexResolver* resolver, +                                  MethodReference ref) { +  // Cheap check that the method is resolved. If it is, we know it's not in +  // a restricted list. +  if (resolver->GetMethod(method_id) != nullptr) { +    return; +  } +  std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id); +  if (hidden_api_.IsInRestrictionList(name)) { +    method_locations_[name].push_back(ref); +  } +} + +void HiddenApiFinder::CheckField(uint32_t field_id, +                                 VeridexResolver* resolver, +                                 MethodReference ref) { +  // Cheap check that the field is resolved. If it is, we know it's not in +  // a restricted list. +  if (resolver->GetField(field_id) != nullptr) { +    return; +  } +  std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id); +  if (hidden_api_.IsInRestrictionList(name)) { +    field_locations_[name].push_back(ref); +  } +} + +void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { +  const DexFile& dex_file = resolver->GetDexFile(); +  size_t class_def_count = dex_file.NumClassDefs(); +  for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { +    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); +    const uint8_t* class_data = dex_file.GetClassData(class_def); +    if (class_data == nullptr) { +      // Empty class. +      continue; +    } +    ClassDataItemIterator it(dex_file, class_data); +    it.SkipAllFields(); +    for (; it.HasNextMethod(); it.Next()) { +      const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); +      if (code_item == nullptr) { +        continue; +      } +      CodeItemDataAccessor code_item_accessor(dex_file, code_item); +      for (const DexInstructionPcPair& inst : code_item_accessor) { +        switch (inst->Opcode()) { +          case Instruction::CONST_CLASS: { +            dex::TypeIndex type_index(inst->VRegB_21c()); +            std::string name = dex_file.StringByTypeIdx(type_index); +            // Only keep classes that are in a restriction list. +            if (hidden_api_.IsInRestrictionList(name)) { +              classes_.insert(name); +            } +            break; +          } +          case Instruction::CONST_STRING: { +            dex::StringIndex string_index(inst->VRegB_21c()); +            std::string name = std::string(dex_file.StringDataByIdx(string_index)); +            // Cheap filtering on the string literal. We know it cannot be a field/method/class +            // if it contains a space. +            if (name.find(' ') == std::string::npos) { +              // Class names at the Java level are of the form x.y.z, but the list encodes +              // them of the form Lx/y/z;. Inner classes have '$' for both Java level class +              // names in strings, and hidden API lists. +              std::string str = name; +              std::replace(str.begin(), str.end(), '.', '/'); +              str = "L" + str + ";"; +              // Note: we can query the lists directly, as HiddenApi added classes that own +              // private methods and fields in them. +              // We don't add class names to the `strings_` set as we know method/field names +              // don't have '.' or '/'. All hidden API class names have a '/'. +              if (hidden_api_.IsInRestrictionList(str)) { +                classes_.insert(str); +              } else if (hidden_api_.IsInRestrictionList(name)) { +                // Could be something passed to JNI. +                classes_.insert(name); +              } else { +                // We only keep track of the location for strings, as these will be the +                // field/method names the user is interested in. +                strings_.insert(name); +                reflection_locations_[name].push_back( +                    MethodReference(&dex_file, it.GetMemberIndex())); +              } +            } +            break; +          } +          case Instruction::INVOKE_DIRECT: +          case Instruction::INVOKE_INTERFACE: +          case Instruction::INVOKE_STATIC: +          case Instruction::INVOKE_SUPER: +          case Instruction::INVOKE_VIRTUAL: { +            CheckMethod( +                inst->VRegB_35c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); +            break; +          } + +          case Instruction::INVOKE_DIRECT_RANGE: +          case Instruction::INVOKE_INTERFACE_RANGE: +          case Instruction::INVOKE_STATIC_RANGE: +          case Instruction::INVOKE_SUPER_RANGE: +          case Instruction::INVOKE_VIRTUAL_RANGE: { +            CheckMethod( +                inst->VRegB_3rc(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); +            break; +          } + +          case Instruction::IGET: +          case Instruction::IGET_WIDE: +          case Instruction::IGET_OBJECT: +          case Instruction::IGET_BOOLEAN: +          case Instruction::IGET_BYTE: +          case Instruction::IGET_CHAR: +          case Instruction::IGET_SHORT: { +            CheckField( +                inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); +            break; +          } + +          case Instruction::IPUT: +          case Instruction::IPUT_WIDE: +          case Instruction::IPUT_OBJECT: +          case Instruction::IPUT_BOOLEAN: +          case Instruction::IPUT_BYTE: +          case Instruction::IPUT_CHAR: +          case Instruction::IPUT_SHORT: { +            CheckField( +                inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); +            break; +          } + +          case Instruction::SGET: +          case Instruction::SGET_WIDE: +          case Instruction::SGET_OBJECT: +          case Instruction::SGET_BOOLEAN: +          case Instruction::SGET_BYTE: +          case Instruction::SGET_CHAR: +          case Instruction::SGET_SHORT: { +            CheckField( +                inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); +            break; +          } + +          case Instruction::SPUT: +          case Instruction::SPUT_WIDE: +          case Instruction::SPUT_OBJECT: +          case Instruction::SPUT_BOOLEAN: +          case Instruction::SPUT_BYTE: +          case Instruction::SPUT_CHAR: +          case Instruction::SPUT_SHORT: { +            CheckField( +                inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); +            break; +          } + +          default: +            break; +        } +      } +    } +  } +} + +static std::string GetApiMethodName(MethodReference ref) { +  return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index); +} + +void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) { +  for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) { +    CollectAccesses(resolver.get()); +  } + +  Dump(std::cout); +} + +void HiddenApiFinder::Dump(std::ostream& os) { +  static const char* kPrefix = "       "; +  uint32_t count = 0; +  uint32_t linking_count = method_locations_.size() + field_locations_.size(); +  uint32_t api_counts[4] = {0, 0, 0, 0}; + +  // Dump methods from hidden APIs linked against. +  for (const std::pair<std::string, std::vector<MethodReference>>& pair : method_locations_) { +    HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first); +    api_counts[api_list]++; +    os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):"; +    os << std::endl; +    for (const MethodReference& ref : pair.second) { +      os << kPrefix << GetApiMethodName(ref) << std::endl; +    } +    os << std::endl; +  } + +  // Dump fields from hidden APIs linked against. +  for (const std::pair<std::string, std::vector<MethodReference>>& pair : field_locations_) { +    HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first); +    api_counts[api_list]++; +    os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):"; +    os << std::endl; +    for (const MethodReference& ref : pair.second) { +      os << kPrefix << GetApiMethodName(ref) << std::endl; +    } +    os << std::endl; +  } + +  // Dump potential reflection uses. +  for (const std::string& cls : classes_) { +    for (const std::string& name : strings_) { +      std::string full_name = cls + "->" + name; +      HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); +      api_counts[api_list]++; +      if (api_list != HiddenApiAccessFlags::kWhitelist) { +        os << "#" << ++count << ": Reflection " << api_list << " " << full_name +           << " potential use(s):"; +        os << std::endl; +        for (const MethodReference& ref : reflection_locations_[name]) { +          os << kPrefix << GetApiMethodName(ref) << std::endl; +        } +        os << std::endl; +      } +    } +  } + +  os << count << " hidden API(s) used: " +     << linking_count << " linked against, " +     << count - linking_count << " potentially through reflection" << std::endl; +  os << kPrefix << api_counts[HiddenApiAccessFlags::kBlacklist] +     << " in blacklist" << std::endl; +  os << kPrefix << api_counts[HiddenApiAccessFlags::kDarkGreylist] +     << " in dark greylist" << std::endl; +  os << kPrefix << api_counts[HiddenApiAccessFlags::kLightGreylist] +     << " in light greylist" << std::endl; +} + +}  // namespace art diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h new file mode 100644 index 0000000000..243079c187 --- /dev/null +++ b/tools/veridex/hidden_api_finder.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software +  * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_ +#define ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_ + +#include "dex/method_reference.h" + +#include <iostream> +#include <map> +#include <set> +#include <string> + +namespace art { + +class HiddenApi; +class VeridexResolver; + +/** + * Reports potential uses of hidden APIs from static linking and reflection. + */ +class HiddenApiFinder { + public: +  explicit HiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {} + +  // Iterate over the dex files associated with the passed resolvers to report +  // hidden API uses. +  void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers); + + private: +  void CollectAccesses(VeridexResolver* resolver); +  void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref); +  void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref); +  void Dump(std::ostream& os); + +  const HiddenApi& hidden_api_; +  std::set<std::string> classes_; +  std::set<std::string> strings_; +  std::map<std::string, std::vector<MethodReference>> reflection_locations_; +  std::map<std::string, std::vector<MethodReference>> method_locations_; +  std::map<std::string, std::vector<MethodReference>> field_locations_; +}; + +}  // namespace art + +#endif  // ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_ diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc index 82978215a6..13dda5c199 100644 --- a/tools/veridex/resolver.cc +++ b/tools/veridex/resolver.cc @@ -18,6 +18,7 @@  #include "dex/dex_file-inl.h"  #include "dex/primitive.h" +#include "hidden_api.h"  #include "veridex.h"  namespace art { diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h index ae94dadb28..06c8aa70c5 100644 --- a/tools/veridex/resolver.h +++ b/tools/veridex/resolver.h @@ -22,6 +22,7 @@  namespace art { +class HiddenApi;  class VeridexResolver;  /** @@ -68,6 +69,11 @@ class VeridexResolver {    // Resolve all type_id/method_id/field_id.    void ResolveAll(); +  // The dex file this resolver is associated to. +  const DexFile& GetDexFile() const { +    return dex_file_; +  } +   private:    // Return the resolver where `kls` is from.    VeridexResolver* GetResolverOf(const VeriClass& kls) const; diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 9287211a3c..16e9f0e55b 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -20,6 +20,8 @@  #include "dex/dex_file.h"  #include "dex/dex_file_loader.h" +#include "hidden_api.h" +#include "hidden_api_finder.h"  #include "resolver.h"  #include <sstream> @@ -161,10 +163,10 @@ class Veridex {      std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;      Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); -    // Resolve all type_id/method_id/field_id of app dex files. -    for (const std::unique_ptr<VeridexResolver>& resolver : app_resolvers) { -      resolver->ResolveAll(); -    } +    // Find and log uses of hidden APIs. +    HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist); +    HiddenApiFinder api_finder(hidden_api); +    api_finder.Run(app_resolvers);      return 0;    }  |