diff options
25 files changed, 1610 insertions, 232 deletions
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index a19b36f9cc..8fd20aa428 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_ #define ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_ +#include <cstring> #include <map> #include "arch/instruction_set.h" @@ -172,11 +173,6 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, return; } - dwarf::Writer<> debug_loc(debug_loc_buffer); - dwarf::Writer<> debug_ranges(debug_ranges_buffer); - debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc.size()); - debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges.size()); - std::vector<VariableLocation> variable_locations = GetVariableLocations( method_info, vreg, @@ -185,6 +181,8 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, dex_pc_high); // Write .debug_loc entries. + dwarf::Writer<> debug_loc(debug_loc_buffer); + const size_t debug_loc_offset = debug_loc.size(); const bool is64bit = Is64BitInstructionSet(isa); std::vector<uint8_t> expr_buffer; for (const VariableLocation& variable_location : variable_locations) { @@ -271,6 +269,8 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, // Write .debug_ranges entries. // This includes ranges where the variable is in scope but the location is not known. + dwarf::Writer<> debug_ranges(debug_ranges_buffer); + size_t debug_ranges_offset = debug_ranges.size(); for (size_t i = 0; i < variable_locations.size(); i++) { uint32_t low_pc = variable_locations[i].low_pc; uint32_t high_pc = variable_locations[i].high_pc; @@ -294,6 +294,23 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, debug_ranges.PushUint32(0); debug_ranges.PushUint32(0); } + + // Simple de-duplication - check whether this entry is same as the last one (or tail of it). + size_t debug_ranges_entry_size = debug_ranges.size() - debug_ranges_offset; + if (debug_ranges_offset >= debug_ranges_entry_size) { + size_t previous_offset = debug_ranges_offset - debug_ranges_entry_size; + if (memcmp(debug_ranges_buffer->data() + previous_offset, + debug_ranges_buffer->data() + debug_ranges_offset, + debug_ranges_entry_size) == 0) { + // Remove what we have just written and use the last entry instead. + debug_ranges_buffer->resize(debug_ranges_offset); + debug_ranges_offset = previous_offset; + } + } + + // Write attributes to .debug_info. + debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc_offset); + debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges_offset); } } // namespace debug diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 3eda8639c1..c500ea4408 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -2107,7 +2107,6 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) { LocationSummary* locations = instruction->GetLocations(); Register res = locations->Out().AsRegister<Register>(); Primitive::Type in_type = instruction->InputAt(0)->GetType(); - bool gt_bias = instruction->IsGtBias(); bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); // 0 if: left == right @@ -2141,6 +2140,7 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) { } case Primitive::kPrimFloat: { + bool gt_bias = instruction->IsGtBias(); FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>(); FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>(); MipsLabel done; @@ -2180,6 +2180,7 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) { break; } case Primitive::kPrimDouble: { + bool gt_bias = instruction->IsGtBias(); FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>(); FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>(); MipsLabel done; @@ -3953,28 +3954,19 @@ void InstructionCodeGeneratorMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDire codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } -void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { - if (TryGenerateIntrinsicCode(invoke, codegen_)) { - return; - } - +void CodeGeneratorMIPS::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { LocationSummary* locations = invoke->GetLocations(); Location receiver = locations->InAt(0); - Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>(); + Register temp = temp_location.AsRegister<Register>(); size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kMipsPointerSize).SizeValue(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMipsWordSize); // temp = object->GetClass(); - if (receiver.IsStackSlot()) { - __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex()); - __ LoadFromOffset(kLoadWord, temp, temp, class_offset); - } else { - DCHECK(receiver.IsRegister()); - __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); - } - codegen_->MaybeRecordImplicitNullCheck(invoke); + DCHECK(receiver.IsRegister()); + __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); + MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetMethodAt(method_offset); __ LoadFromOffset(kLoadWord, temp, temp, method_offset); // T9 = temp->GetEntryPoint(); @@ -3982,6 +3974,14 @@ void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { // T9(); __ Jalr(T9); __ Nop(); +} + +void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { + if (TryGenerateIntrinsicCode(invoke, codegen_)) { + return; + } + + codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 12964b0b6a..dd0641c7ca 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -353,10 +353,7 @@ class CodeGeneratorMIPS : public CodeGenerator { MethodReference target_method) OVERRIDE; void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp); - void GenerateVirtualCall(HInvokeVirtual* invoke ATTRIBUTE_UNUSED, - Location temp ATTRIBUTE_UNUSED) OVERRIDE { - UNIMPLEMENTED(FATAL) << "Not implemented on MIPS"; - } + void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; void MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED, Primitive::Type type ATTRIBUTE_UNUSED) OVERRIDE { diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 119084e026..e3a44f1c96 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1727,7 +1727,6 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) { LocationSummary* locations = instruction->GetLocations(); GpuRegister res = locations->Out().AsRegister<GpuRegister>(); Primitive::Type in_type = instruction->InputAt(0)->GetType(); - bool gt_bias = instruction->IsGtBias(); // 0 if: left == right // 1 if: left > right @@ -1769,7 +1768,7 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) { __ CmpEqS(FTMP, lhs, rhs); __ LoadConst32(res, 0); __ Bc1nez(FTMP, &done); - if (gt_bias) { + if (instruction->IsGtBias()) { __ CmpLtS(FTMP, lhs, rhs); __ LoadConst32(res, -1); __ Bc1nez(FTMP, &done); @@ -1791,7 +1790,7 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) { __ CmpEqD(FTMP, lhs, rhs); __ LoadConst32(res, 0); __ Bc1nez(FTMP, &done); - if (gt_bias) { + if (instruction->IsGtBias()) { __ CmpLtD(FTMP, lhs, rhs); __ LoadConst32(res, -1); __ Bc1nez(FTMP, &done); @@ -4258,4 +4257,3 @@ void InstructionCodeGeneratorMIPS64::VisitClassTableGet(HClassTableGet*) { } // namespace mips64 } // namespace art - diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 1161253792..eb7315aa7a 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -349,7 +349,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { void MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED, Primitive::Type type ATTRIBUTE_UNUSED) OVERRIDE { - UNIMPLEMENTED(FATAL); + UNIMPLEMENTED(FATAL) << "Not implemented on MIPS64"; } private: diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index 57452cc076..014353d615 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -178,7 +178,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitCompare(HCompare* instruction ((input_cst->IsFloatConstant() && input_cst->AsFloatConstant()->IsNaN()) || (input_cst->IsDoubleConstant() && input_cst->AsDoubleConstant()->IsNaN()))) { // Replace code looking like - // CMP{G,L} dst, src, NaN + // CMP{G,L}-{FLOAT,DOUBLE} dst, src, NaN // with // CONSTANT +1 (gt bias) // or diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 4cf0eb1565..c0263e4e5b 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -384,6 +384,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << array_set->GetValueCanBeNull() << std::noboolalpha; } + void VisitCompare(HCompare* compare) OVERRIDE { + ComparisonBias bias = compare->GetBias(); + StartAttributeStream("bias") << (bias == ComparisonBias::kGtBias + ? "gt" + : (bias == ComparisonBias::kLtBias ? "lt" : "none")); + } + void VisitInvoke(HInvoke* invoke) OVERRIDE { StartAttributeStream("dex_file_index") << invoke->GetDexMethodIndex(); StartAttributeStream("method_name") << PrettyMethod( diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 2294713a3e..5d6e8c280f 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -113,11 +113,10 @@ class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS { if (invoke_->IsInvokeStaticOrDirect()) { codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), Location::RegisterLocation(A0)); - codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); } else { - UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented"; - UNREACHABLE(); + codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(A0)); } + codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); // Copy the result back to the expected output. Location out = invoke_->GetLocations()->Out(); @@ -825,6 +824,220 @@ void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) { GetAssembler()); } +// byte libcore.io.Memory.peekByte(long address) +void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekByte(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekByte(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); + Register out = invoke->GetLocations()->Out().AsRegister<Register>(); + + __ Lb(out, adr, 0); +} + +// short libcore.io.Memory.peekShort(long address) +void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekShortNative(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekShortNative(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); + Register out = invoke->GetLocations()->Out().AsRegister<Register>(); + + if (IsR6()) { + __ Lh(out, adr, 0); + } else if (IsR2OrNewer()) { + // Unlike for words, there are no lhl/lhr instructions to load + // unaligned halfwords so the code loads individual bytes, in case + // the address isn't halfword-aligned, and assembles them into a + // signed halfword. + __ Lb(AT, adr, 1); // This byte must be sign-extended. + __ Lb(out, adr, 0); // This byte can be either sign-extended, or + // zero-extended because the following + // instruction overwrites the sign bits. + __ Ins(out, AT, 8, 24); + } else { + __ Lbu(AT, adr, 0); // This byte must be zero-extended. If it's not + // the "or" instruction below will destroy the upper + // 24 bits of the final result. + __ Lb(out, adr, 1); // This byte must be sign-extended. + __ Sll(out, out, 8); + __ Or(out, out, AT); + } +} + +// int libcore.io.Memory.peekInt(long address) +void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekIntNative(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap); +} + +void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekIntNative(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); + Register out = invoke->GetLocations()->Out().AsRegister<Register>(); + + if (IsR6()) { + __ Lw(out, adr, 0); + } else { + __ Lwr(out, adr, 0); + __ Lwl(out, adr, 3); + } +} + +// long libcore.io.Memory.peekLong(long address) +void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekLongNative(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap); +} + +void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekLongNative(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); + Register out_lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>(); + Register out_hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>(); + + if (IsR6()) { + __ Lw(out_lo, adr, 0); + __ Lw(out_hi, adr, 4); + } else { + __ Lwr(out_lo, adr, 0); + __ Lwl(out_lo, adr, 3); + __ Lwr(out_hi, adr, 4); + __ Lwl(out_hi, adr, 7); + } +} + +static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); +} + +// void libcore.io.Memory.pokeByte(long address, byte value) +void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeByte(HInvoke* invoke) { + CreateIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeByte(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); + Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>(); + + __ Sb(val, adr, 0); +} + +// void libcore.io.Memory.pokeShort(long address, short value) +void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeShortNative(HInvoke* invoke) { + CreateIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeShortNative(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); + Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>(); + + if (IsR6()) { + __ Sh(val, adr, 0); + } else { + // Unlike for words, there are no shl/shr instructions to store + // unaligned halfwords so the code stores individual bytes, in case + // the address isn't halfword-aligned. + __ Sb(val, adr, 0); + __ Srl(AT, val, 8); + __ Sb(AT, adr, 1); + } +} + +// void libcore.io.Memory.pokeInt(long address, int value) +void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeIntNative(HInvoke* invoke) { + CreateIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeIntNative(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); + Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>(); + + if (IsR6()) { + __ Sw(val, adr, 0); + } else { + __ Swr(val, adr, 0); + __ Swl(val, adr, 3); + } +} + +// void libcore.io.Memory.pokeLong(long address, long value) +void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) { + CreateIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); + Register val_lo = invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(); + Register val_hi = invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(); + + if (IsR6()) { + __ Sw(val_lo, adr, 0); + __ Sw(val_hi, adr, 4); + } else { + __ Swr(val_lo, adr, 0); + __ Swl(val_lo, adr, 3); + __ Swr(val_hi, adr, 4); + __ Swl(val_hi, adr, 7); + } +} + +// char java.lang.String.charAt(int index) +void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) { + LocationSummary* locations = invoke->GetLocations(); + MipsAssembler* assembler = GetAssembler(); + + // Location of reference to data array + const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); + // Location of count + const int32_t count_offset = mirror::String::CountOffset().Int32Value(); + + Register obj = locations->InAt(0).AsRegister<Register>(); + Register idx = locations->InAt(1).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + + // TODO: Maybe we can support range check elimination. Overall, + // though, I think it's not worth the cost. + // TODO: For simplicity, the index parameter is requested in a + // register, so different from Quick we will not optimize the + // code for constants (which would save a register). + + SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); + codegen_->AddSlowPath(slow_path); + + // Load the string size + __ Lw(TMP, obj, count_offset); + codegen_->MaybeRecordImplicitNullCheck(invoke); + // Revert to slow path if idx is too large, or negative + __ Bgeu(idx, TMP, slow_path->GetEntryLabel()); + + // out = obj[2*idx]. + __ Sll(TMP, idx, 1); // idx * 2 + __ Addu(TMP, TMP, obj); // Address of char at location idx + __ Lhu(out, TMP, value_offset); // Load char at location idx + + __ Bind(slow_path->GetExitLabel()); +} + // boolean java.lang.String.equals(Object anObject) void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, @@ -956,14 +1169,6 @@ UNIMPLEMENTED_INTRINSIC(MathFloor) UNIMPLEMENTED_INTRINSIC(MathRint) UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(MemoryPeekByte) -UNIMPLEMENTED_INTRINSIC(MemoryPeekIntNative) -UNIMPLEMENTED_INTRINSIC(MemoryPeekLongNative) -UNIMPLEMENTED_INTRINSIC(MemoryPeekShortNative) -UNIMPLEMENTED_INTRINSIC(MemoryPokeByte) -UNIMPLEMENTED_INTRINSIC(MemoryPokeIntNative) -UNIMPLEMENTED_INTRINSIC(MemoryPokeLongNative) -UNIMPLEMENTED_INTRINSIC(MemoryPokeShortNative) UNIMPLEMENTED_INTRINSIC(ThreadCurrentThread) UNIMPLEMENTED_INTRINSIC(UnsafeGet) UNIMPLEMENTED_INTRINSIC(UnsafeGetVolatile) @@ -983,7 +1188,6 @@ UNIMPLEMENTED_INTRINSIC(UnsafePutLongVolatile) UNIMPLEMENTED_INTRINSIC(UnsafeCASInt) UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) UNIMPLEMENTED_INTRINSIC(UnsafeCASObject) -UNIMPLEMENTED_INTRINSIC(StringCharAt) UNIMPLEMENTED_INTRINSIC(StringCompareTo) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 27015c0ac6..b26ce0aa13 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -15,6 +15,8 @@ */ #include "nodes.h" +#include <cfloat> + #include "code_generator.h" #include "common_dominator.h" #include "ssa_builder.h" @@ -27,6 +29,12 @@ namespace art { +// Enable floating-point static evaluation during constant folding +// only if all floating-point operations and constants evaluate in the +// range and precision of the type used (i.e., 32-bit float, 64-bit +// double). +static constexpr bool kEnableFloatingPointStaticEvaluation = (FLT_EVAL_METHOD == 0); + void HGraph::InitializeInexactObjectRTI(StackHandleScopeCollection* handles) { ScopedObjectAccess soa(Thread::Current()); // Create the inexact Object reference type and store it in the HGraph. @@ -1159,6 +1167,12 @@ HConstant* HUnaryOperation::TryStaticEvaluation() const { return Evaluate(GetInput()->AsIntConstant()); } else if (GetInput()->IsLongConstant()) { return Evaluate(GetInput()->AsLongConstant()); + } else if (kEnableFloatingPointStaticEvaluation) { + if (GetInput()->IsFloatConstant()) { + return Evaluate(GetInput()->AsFloatConstant()); + } else if (GetInput()->IsDoubleConstant()) { + return Evaluate(GetInput()->AsDoubleConstant()); + } } return nullptr; } @@ -1178,6 +1192,12 @@ HConstant* HBinaryOperation::TryStaticEvaluation() const { } } else if (GetLeft()->IsNullConstant() && GetRight()->IsNullConstant()) { return Evaluate(GetLeft()->AsNullConstant(), GetRight()->AsNullConstant()); + } else if (kEnableFloatingPointStaticEvaluation) { + if (GetLeft()->IsFloatConstant() && GetRight()->IsFloatConstant()) { + return Evaluate(GetLeft()->AsFloatConstant(), GetRight()->AsFloatConstant()); + } else if (GetLeft()->IsDoubleConstant() && GetRight()->IsDoubleConstant()) { + return Evaluate(GetLeft()->AsDoubleConstant(), GetRight()->AsDoubleConstant()); + } } return nullptr; } @@ -1205,6 +1225,20 @@ HInstruction* HBinaryOperation::GetLeastConstantLeft() const { } } +std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs) { + switch (rhs) { + case ComparisonBias::kNoBias: + return os << "no_bias"; + case ComparisonBias::kGtBias: + return os << "gt_bias"; + case ComparisonBias::kLtBias: + return os << "lt_bias"; + default: + LOG(FATAL) << "Unknown ComparisonBias: " << static_cast<int>(rhs); + UNREACHABLE(); + } +} + bool HCondition::IsBeforeWhenDisregardMoves(HInstruction* instruction) const { return this == instruction->GetPreviousDisregardingMoves(); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 854854f238..01ba704610 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2394,7 +2394,7 @@ class HIntConstant : public HConstant { } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - DCHECK(other->IsIntConstant()); + DCHECK(other->IsIntConstant()) << other->DebugName(); return other->AsIntConstant()->value_ == value_; } @@ -2427,7 +2427,7 @@ class HLongConstant : public HConstant { uint64_t GetValueAsUint64() const OVERRIDE { return value_; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - DCHECK(other->IsLongConstant()); + DCHECK(other->IsLongConstant()) << other->DebugName(); return other->AsLongConstant()->value_ == value_; } @@ -2449,6 +2449,92 @@ class HLongConstant : public HConstant { DISALLOW_COPY_AND_ASSIGN(HLongConstant); }; +class HFloatConstant : public HConstant { + public: + float GetValue() const { return value_; } + + uint64_t GetValueAsUint64() const OVERRIDE { + return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_)); + } + + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + DCHECK(other->IsFloatConstant()) << other->DebugName(); + return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64(); + } + + size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } + + bool IsMinusOne() const OVERRIDE { + return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>((-1.0f)); + } + bool IsZero() const OVERRIDE { + return value_ == 0.0f; + } + bool IsOne() const OVERRIDE { + return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(1.0f); + } + bool IsNaN() const { + return std::isnan(value_); + } + + DECLARE_INSTRUCTION(FloatConstant); + + private: + explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc) + : HConstant(Primitive::kPrimFloat, dex_pc), value_(value) {} + explicit HFloatConstant(int32_t value, uint32_t dex_pc = kNoDexPc) + : HConstant(Primitive::kPrimFloat, dex_pc), value_(bit_cast<float, int32_t>(value)) {} + + const float value_; + + // Only the SsaBuilder and HGraph can create floating-point constants. + friend class SsaBuilder; + friend class HGraph; + DISALLOW_COPY_AND_ASSIGN(HFloatConstant); +}; + +class HDoubleConstant : public HConstant { + public: + double GetValue() const { return value_; } + + uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); } + + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + DCHECK(other->IsDoubleConstant()) << other->DebugName(); + return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64(); + } + + size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } + + bool IsMinusOne() const OVERRIDE { + return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((-1.0)); + } + bool IsZero() const OVERRIDE { + return value_ == 0.0; + } + bool IsOne() const OVERRIDE { + return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>(1.0); + } + bool IsNaN() const { + return std::isnan(value_); + } + + DECLARE_INSTRUCTION(DoubleConstant); + + private: + explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc) + : HConstant(Primitive::kPrimDouble, dex_pc), value_(value) {} + explicit HDoubleConstant(int64_t value, uint32_t dex_pc = kNoDexPc) + : HConstant(Primitive::kPrimDouble, dex_pc), value_(bit_cast<double, int64_t>(value)) {} + + const double value_; + + // Only the SsaBuilder and HGraph can create floating-point constants. + friend class SsaBuilder; + friend class HGraph; + DISALLOW_COPY_AND_ASSIGN(HDoubleConstant); +}; + // Conditional branch. A block ending with an HIf instruction must have // two successors. class HIf : public HTemplateInstruction<1> { @@ -2650,14 +2736,16 @@ class HUnaryOperation : public HExpression<1> { return true; } - // Try to statically evaluate `operation` and return a HConstant - // containing the result of this evaluation. If `operation` cannot + // Try to statically evaluate `this` and return a HConstant + // containing the result of this evaluation. If `this` cannot // be evaluated as a constant, return null. HConstant* TryStaticEvaluation() const; // Apply this operation to `x`. virtual HConstant* Evaluate(HIntConstant* x) const = 0; virtual HConstant* Evaluate(HLongConstant* x) const = 0; + virtual HConstant* Evaluate(HFloatConstant* x) const = 0; + virtual HConstant* Evaluate(HDoubleConstant* x) const = 0; DECLARE_ABSTRACT_INSTRUCTION(UnaryOperation); @@ -2720,12 +2808,17 @@ class HBinaryOperation : public HExpression<2> { return true; } - // Try to statically evaluate `operation` and return a HConstant - // containing the result of this evaluation. If `operation` cannot + // Try to statically evaluate `this` and return a HConstant + // containing the result of this evaluation. If `this` cannot // be evaluated as a constant, return null. HConstant* TryStaticEvaluation() const; // Apply this operation to `x` and `y`. + virtual HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED, + HNullConstant* y ATTRIBUTE_UNUSED) const { + VLOG(compiler) << DebugName() << " is not defined for the (null, null) case."; + return nullptr; + } virtual HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const = 0; virtual HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const = 0; virtual HConstant* Evaluate(HIntConstant* x ATTRIBUTE_UNUSED, @@ -2738,11 +2831,8 @@ class HBinaryOperation : public HExpression<2> { VLOG(compiler) << DebugName() << " is not defined for the (long, int) case."; return nullptr; } - virtual HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED, - HNullConstant* y ATTRIBUTE_UNUSED) const { - VLOG(compiler) << DebugName() << " is not defined for the (null, null) case."; - return nullptr; - } + virtual HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const = 0; + virtual HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const = 0; // Returns an input that can legally be used as the right input and is // constant, or null. @@ -2766,6 +2856,8 @@ enum class ComparisonBias { kLtBias, // return -1 for NaN comparisons }; +std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); + class HCondition : public HBinaryOperation { public: HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) @@ -2783,7 +2875,7 @@ class HCondition : public HBinaryOperation { virtual IfCondition GetOppositeCondition() const = 0; bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; } - + ComparisonBias GetBias() const { return bias_; } void SetBias(ComparisonBias bias) { bias_ = bias; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { @@ -2791,17 +2883,34 @@ class HCondition : public HBinaryOperation { } bool IsFPConditionTrueIfNaN() const { - DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())); + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); IfCondition if_cond = GetCondition(); return IsGtBias() ? ((if_cond == kCondGT) || (if_cond == kCondGE)) : (if_cond == kCondNE); } bool IsFPConditionFalseIfNaN() const { - DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())); + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); IfCondition if_cond = GetCondition(); return IsGtBias() ? ((if_cond == kCondLT) || (if_cond == kCondLE)) : (if_cond == kCondEQ); } + protected: + template <typename T> + int32_t Compare(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); } + + template <typename T> + int32_t CompareFP(T x, T y) const { + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); + DCHECK_NE(GetBias(), ComparisonBias::kNoBias); + // Handle the bias. + return std::isunordered(x, y) ? (IsGtBias() ? 1 : -1) : Compare(x, y); + } + + // Return an integer constant containing the result of a condition evaluated at compile time. + HIntConstant* MakeConstantCondition(bool value, uint32_t dex_pc) const { + return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc); + } + private: // Needed if we merge a HCompare into a HCondition. ComparisonBias bias_; @@ -2817,17 +2926,25 @@ class HEqual : public HCondition { bool IsCommutative() const OVERRIDE { return true; } + HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED, + HNullConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + return MakeConstantCondition(true, GetDexPc()); + } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + // In the following Evaluate methods, a HCompare instruction has + // been merged into this HEqual instruction; evaluate it as + // `Compare(x, y) == 0`. HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), + GetDexPc()); } - HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED, - HNullConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant(1); + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); } DECLARE_INSTRUCTION(Equal); @@ -2853,17 +2970,24 @@ class HNotEqual : public HCondition { bool IsCommutative() const OVERRIDE { return true; } + HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED, + HNullConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + return MakeConstantCondition(false, GetDexPc()); + } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + // In the following Evaluate methods, a HCompare instruction has + // been merged into this HNotEqual instruction; evaluate it as + // `Compare(x, y) != 0`. HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc()); } - HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED, - HNullConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant(0); + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); } DECLARE_INSTRUCTION(NotEqual); @@ -2888,12 +3012,19 @@ class HLessThan : public HCondition { : HCondition(first, second, dex_pc) {} HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + // In the following Evaluate methods, a HCompare instruction has + // been merged into this HLessThan instruction; evaluate it as + // `Compare(x, y) < 0`. HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); } DECLARE_INSTRUCTION(LessThan); @@ -2918,12 +3049,19 @@ class HLessThanOrEqual : public HCondition { : HCondition(first, second, dex_pc) {} HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + // In the following Evaluate methods, a HCompare instruction has + // been merged into this HLessThanOrEqual instruction; evaluate it as + // `Compare(x, y) <= 0`. HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); } DECLARE_INSTRUCTION(LessThanOrEqual); @@ -2948,12 +3086,19 @@ class HGreaterThan : public HCondition { : HCondition(first, second, dex_pc) {} HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + // In the following Evaluate methods, a HCompare instruction has + // been merged into this HGreaterThan instruction; evaluate it as + // `Compare(x, y) > 0`. HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); } DECLARE_INSTRUCTION(GreaterThan); @@ -2978,12 +3123,19 @@ class HGreaterThanOrEqual : public HCondition { : HCondition(first, second, dex_pc) {} HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + // In the following Evaluate methods, a HCompare instruction has + // been merged into this HGreaterThanOrEqual instruction; evaluate it as + // `Compare(x, y) >= 0`. HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc()); } DECLARE_INSTRUCTION(GreaterThanOrEqual); @@ -3008,14 +3160,20 @@ class HBelow : public HCondition { : HCondition(first, second, dex_pc) {} HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(static_cast<uint32_t>(x->GetValue()), - static_cast<uint32_t>(y->GetValue())), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(static_cast<uint64_t>(x->GetValue()), - static_cast<uint64_t>(y->GetValue())), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); } DECLARE_INSTRUCTION(Below); @@ -3029,7 +3187,9 @@ class HBelow : public HCondition { } private: - template <typename T> bool Compute(T x, T y) const { return x < y; } + template <typename T> bool Compute(T x, T y) const { + return MakeUnsigned(x) < MakeUnsigned(y); + } DISALLOW_COPY_AND_ASSIGN(HBelow); }; @@ -3040,14 +3200,20 @@ class HBelowOrEqual : public HCondition { : HCondition(first, second, dex_pc) {} HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(static_cast<uint32_t>(x->GetValue()), - static_cast<uint32_t>(y->GetValue())), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(static_cast<uint64_t>(x->GetValue()), - static_cast<uint64_t>(y->GetValue())), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); } DECLARE_INSTRUCTION(BelowOrEqual); @@ -3061,7 +3227,9 @@ class HBelowOrEqual : public HCondition { } private: - template <typename T> bool Compute(T x, T y) const { return x <= y; } + template <typename T> bool Compute(T x, T y) const { + return MakeUnsigned(x) <= MakeUnsigned(y); + } DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual); }; @@ -3072,14 +3240,20 @@ class HAbove : public HCondition { : HCondition(first, second, dex_pc) {} HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(static_cast<uint32_t>(x->GetValue()), - static_cast<uint32_t>(y->GetValue())), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(static_cast<uint64_t>(x->GetValue()), - static_cast<uint64_t>(y->GetValue())), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); } DECLARE_INSTRUCTION(Above); @@ -3093,7 +3267,9 @@ class HAbove : public HCondition { } private: - template <typename T> bool Compute(T x, T y) const { return x > y; } + template <typename T> bool Compute(T x, T y) const { + return MakeUnsigned(x) > MakeUnsigned(y); + } DISALLOW_COPY_AND_ASSIGN(HAbove); }; @@ -3104,14 +3280,20 @@ class HAboveOrEqual : public HCondition { : HCondition(first, second, dex_pc) {} HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(static_cast<uint32_t>(x->GetValue()), - static_cast<uint32_t>(y->GetValue())), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(static_cast<uint64_t>(x->GetValue()), - static_cast<uint64_t>(y->GetValue())), GetDexPc()); + return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); } DECLARE_INSTRUCTION(AboveOrEqual); @@ -3125,7 +3307,9 @@ class HAboveOrEqual : public HCondition { } private: - template <typename T> bool Compute(T x, T y) const { return x >= y; } + template <typename T> bool Compute(T x, T y) const { + return MakeUnsigned(x) >= MakeUnsigned(y); + } DISALLOW_COPY_AND_ASSIGN(HAboveOrEqual); }; @@ -3150,15 +3334,32 @@ class HCompare : public HBinaryOperation { } template <typename T> - int32_t Compute(T x, T y) const { return x == y ? 0 : x > y ? 1 : -1; } + int32_t Compute(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); } + + template <typename T> + int32_t ComputeFP(T x, T y) const { + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); + DCHECK_NE(GetBias(), ComparisonBias::kNoBias); + // Handle the bias. + return std::isunordered(x, y) ? (IsGtBias() ? 1 : -1) : Compute(x, y); + } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + // Note that there is no "cmp-int" Dex instruction so we shouldn't + // reach this code path when processing a freshly built HIR + // graph. However HCompare integer instructions can be synthesized + // by the instruction simplifier to implement IntegerCompare and + // IntegerSignum intrinsics, so we have to handle this case. + return MakeConstantComparison(Compute(x->GetValue(), y->GetValue()), GetDexPc()); } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + return MakeConstantComparison(Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return MakeConstantComparison(ComputeFP(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return MakeConstantComparison(ComputeFP(x->GetValue(), y->GetValue()), GetDexPc()); } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { @@ -3167,8 +3368,12 @@ class HCompare : public HBinaryOperation { ComparisonBias GetBias() const { return bias_; } - bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; } - + // Does this compare instruction have a "gt bias" (vs an "lt bias")? + // Only meaninfgul for floating-point comparisons. + bool IsGtBias() const { + DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); + return bias_ == ComparisonBias::kGtBias; + } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) { // MIPS64 uses a runtime call for FP comparisons. @@ -3177,6 +3382,13 @@ class HCompare : public HBinaryOperation { DECLARE_INSTRUCTION(Compare); + protected: + // Return an integer constant containing the result of a comparison evaluated at compile time. + HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const { + DCHECK(value == -1 || value == 0 || value == 1) << value; + return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc); + } + private: const ComparisonBias bias_; @@ -3234,92 +3446,6 @@ class HStoreLocal : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HStoreLocal); }; -class HFloatConstant : public HConstant { - public: - float GetValue() const { return value_; } - - uint64_t GetValueAsUint64() const OVERRIDE { - return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_)); - } - - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - DCHECK(other->IsFloatConstant()); - return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64(); - } - - size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } - - bool IsMinusOne() const OVERRIDE { - return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>((-1.0f)); - } - bool IsZero() const OVERRIDE { - return value_ == 0.0f; - } - bool IsOne() const OVERRIDE { - return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(1.0f); - } - bool IsNaN() const { - return std::isnan(value_); - } - - DECLARE_INSTRUCTION(FloatConstant); - - private: - explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc) - : HConstant(Primitive::kPrimFloat, dex_pc), value_(value) {} - explicit HFloatConstant(int32_t value, uint32_t dex_pc = kNoDexPc) - : HConstant(Primitive::kPrimFloat, dex_pc), value_(bit_cast<float, int32_t>(value)) {} - - const float value_; - - // Only the SsaBuilder and HGraph can create floating-point constants. - friend class SsaBuilder; - friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HFloatConstant); -}; - -class HDoubleConstant : public HConstant { - public: - double GetValue() const { return value_; } - - uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); } - - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - DCHECK(other->IsDoubleConstant()); - return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64(); - } - - size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } - - bool IsMinusOne() const OVERRIDE { - return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((-1.0)); - } - bool IsZero() const OVERRIDE { - return value_ == 0.0; - } - bool IsOne() const OVERRIDE { - return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>(1.0); - } - bool IsNaN() const { - return std::isnan(value_); - } - - DECLARE_INSTRUCTION(DoubleConstant); - - private: - explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc) - : HConstant(Primitive::kPrimDouble, dex_pc), value_(value) {} - explicit HDoubleConstant(int64_t value, uint32_t dex_pc = kNoDexPc) - : HConstant(Primitive::kPrimDouble, dex_pc), value_(bit_cast<double, int64_t>(value)) {} - - const double value_; - - // Only the SsaBuilder and HGraph can create floating-point constants. - friend class SsaBuilder; - friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HDoubleConstant); -}; - class HNewInstance : public HExpression<2> { public: HNewInstance(HInstruction* cls, @@ -3871,6 +3997,12 @@ class HNeg : public HUnaryOperation { HConstant* Evaluate(HLongConstant* x) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetFloatConstant(Compute(x->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetDoubleConstant(Compute(x->GetValue()), GetDexPc()); + } DECLARE_INSTRUCTION(Neg); @@ -3937,6 +4069,14 @@ class HAdd : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetFloatConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetDoubleConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } DECLARE_INSTRUCTION(Add); @@ -3962,6 +4102,14 @@ class HSub : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetFloatConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetDoubleConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } DECLARE_INSTRUCTION(Sub); @@ -3989,6 +4137,14 @@ class HMul : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetFloatConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetDoubleConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } DECLARE_INSTRUCTION(Mul); @@ -4005,7 +4161,8 @@ class HDiv : public HBinaryOperation { : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {} template <typename T> - T Compute(T x, T y) const { + T ComputeIntegral(T x, T y) const { + DCHECK(!Primitive::IsFloatingPointType(GetType())) << GetType(); // Our graph structure ensures we never have 0 for `y` during // constant folding. DCHECK_NE(y, 0); @@ -4013,13 +4170,27 @@ class HDiv : public HBinaryOperation { return (y == -1) ? -x : x / y; } + template <typename T> + T ComputeFP(T x, T y) const { + DCHECK(Primitive::IsFloatingPointType(GetType())) << GetType(); + return x / y; + } + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetFloatConstant( + ComputeFP(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetDoubleConstant( + ComputeFP(x->GetValue(), y->GetValue()), GetDexPc()); } static SideEffects SideEffectsForArchRuntimeCalls() { @@ -4042,7 +4213,8 @@ class HRem : public HBinaryOperation { : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {} template <typename T> - T Compute(T x, T y) const { + T ComputeIntegral(T x, T y) const { + DCHECK(!Primitive::IsFloatingPointType(GetType())) << GetType(); // Our graph structure ensures we never have 0 for `y` during // constant folding. DCHECK_NE(y, 0); @@ -4050,15 +4222,28 @@ class HRem : public HBinaryOperation { return (y == -1) ? 0 : x % y; } + template <typename T> + T ComputeFP(T x, T y) const { + DCHECK(Primitive::IsFloatingPointType(GetType())) << GetType(); + return std::fmod(x, y); + } + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetFloatConstant( + ComputeFP(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetDoubleConstant( + ComputeFP(x->GetValue(), y->GetValue()), GetDexPc()); } - static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); @@ -4125,6 +4310,16 @@ class HShl : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(Shl); @@ -4161,6 +4356,16 @@ class HShr : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(Shr); @@ -4198,6 +4403,16 @@ class HUShr : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(UShr); @@ -4234,6 +4449,16 @@ class HAnd : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(And); @@ -4270,6 +4495,16 @@ class HOr : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(Or); @@ -4306,6 +4541,16 @@ class HXor : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(Xor); @@ -4344,6 +4589,16 @@ class HRor : public HBinaryOperation { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(Ror); @@ -4410,6 +4665,14 @@ class HNot : public HUnaryOperation { HConstant* Evaluate(HLongConstant* x) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc()); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(Not); @@ -4428,7 +4691,7 @@ class HBooleanNot : public HUnaryOperation { } template <typename T> bool Compute(T x) const { - DCHECK(IsUint<1>(x)); + DCHECK(IsUint<1>(x)) << x; return !x; } @@ -4439,6 +4702,14 @@ class HBooleanNot : public HUnaryOperation { LOG(FATAL) << DebugName() << " is not defined for long values"; UNREACHABLE(); } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } DECLARE_INSTRUCTION(BooleanNot); @@ -4786,10 +5057,10 @@ class HArrayGet : public HExpression<2> { DCHECK_EQ(GetArray(), other->GetArray()); DCHECK_EQ(GetIndex(), other->GetIndex()); if (Primitive::IsIntOrLongType(GetType())) { - DCHECK(Primitive::IsFloatingPointType(other->GetType())); + DCHECK(Primitive::IsFloatingPointType(other->GetType())) << other->GetType(); } else { - DCHECK(Primitive::IsFloatingPointType(GetType())); - DCHECK(Primitive::IsIntOrLongType(other->GetType())); + DCHECK(Primitive::IsFloatingPointType(GetType())) << GetType(); + DCHECK(Primitive::IsIntOrLongType(other->GetType())) << other->GetType(); } } return result; @@ -6004,7 +6275,7 @@ inline int64_t Int64FromConstant(HConstant* constant) { } else if (constant->IsLongConstant()) { return constant->AsLongConstant()->GetValue(); } else { - DCHECK(constant->IsNullConstant()); + DCHECK(constant->IsNullConstant()) << constant->DebugName(); return 0; } } diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index ac9c097892..6fd65ee9a4 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -426,6 +426,16 @@ void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) { EmitI(0x23, rs, rt, imm16); } +void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) { + CHECK(!IsR6()); + EmitI(0x22, rs, rt, imm16); +} + +void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) { + CHECK(!IsR6()); + EmitI(0x26, rs, rt, imm16); +} + void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) { EmitI(0x24, rs, rt, imm16); } @@ -465,6 +475,16 @@ void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) { EmitI(0x2b, rs, rt, imm16); } +void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) { + CHECK(!IsR6()); + EmitI(0x2a, rs, rt, imm16); +} + +void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) { + CHECK(!IsR6()); + EmitI(0x2e, rs, rt, imm16); +} + void MipsAssembler::Slt(Register rd, Register rs, Register rt) { EmitR(0, rs, rt, rd, 0, 0x2a); } diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 01c6490f88..2262af49b3 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -162,6 +162,8 @@ class MipsAssembler FINAL : public Assembler { void Lb(Register rt, Register rs, uint16_t imm16); void Lh(Register rt, Register rs, uint16_t imm16); void Lw(Register rt, Register rs, uint16_t imm16); + void Lwl(Register rt, Register rs, uint16_t imm16); + void Lwr(Register rt, Register rs, uint16_t imm16); void Lbu(Register rt, Register rs, uint16_t imm16); void Lhu(Register rt, Register rs, uint16_t imm16); void Lui(Register rt, uint16_t imm16); @@ -172,6 +174,8 @@ class MipsAssembler FINAL : public Assembler { void Sb(Register rt, Register rs, uint16_t imm16); void Sh(Register rt, Register rs, uint16_t imm16); void Sw(Register rt, Register rs, uint16_t imm16); + void Swl(Register rt, Register rs, uint16_t imm16); + void Swr(Register rt, Register rs, uint16_t imm16); void Slt(Register rd, Register rs, Register rt); void Sltu(Register rd, Register rs, Register rt); diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index 5fc3deebd3..9e27f07ff2 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -335,6 +335,18 @@ TEST_F(AssemblerMIPSTest, Nor) { DriverStr(RepeatRRR(&mips::MipsAssembler::Nor, "nor ${reg1}, ${reg2}, ${reg3}"), "Nor"); } +////////// +// MISC // +////////// + +TEST_F(AssemblerMIPSTest, Movz) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Movz, "movz ${reg1}, ${reg2}, ${reg3}"), "Movz"); +} + +TEST_F(AssemblerMIPSTest, Movn) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Movn, "movn ${reg1}, ${reg2}, ${reg3}"), "Movn"); +} + TEST_F(AssemblerMIPSTest, Seb) { DriverStr(RepeatRR(&mips::MipsAssembler::Seb, "seb ${reg1}, ${reg2}"), "Seb"); } @@ -363,6 +375,10 @@ TEST_F(AssemblerMIPSTest, Srlv) { DriverStr(RepeatRRR(&mips::MipsAssembler::Srlv, "srlv ${reg1}, ${reg2}, ${reg3}"), "Srlv"); } +TEST_F(AssemblerMIPSTest, Rotrv) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Rotrv, "rotrv ${reg1}, ${reg2}, ${reg3}"), "rotrv"); +} + TEST_F(AssemblerMIPSTest, Srav) { DriverStr(RepeatRRR(&mips::MipsAssembler::Srav, "srav ${reg1}, ${reg2}, ${reg3}"), "Srav"); } @@ -405,6 +421,14 @@ TEST_F(AssemblerMIPSTest, Ext) { DriverStr(expected, "Ext"); } +TEST_F(AssemblerMIPSTest, ClzR2) { + DriverStr(RepeatRR(&mips::MipsAssembler::ClzR2, "clz ${reg1}, ${reg2}"), "clzR2"); +} + +TEST_F(AssemblerMIPSTest, CloR2) { + DriverStr(RepeatRR(&mips::MipsAssembler::CloR2, "clo ${reg1}, ${reg2}"), "cloR2"); +} + TEST_F(AssemblerMIPSTest, Lb) { DriverStr(RepeatRRIb(&mips::MipsAssembler::Lb, -16, "lb ${reg1}, {imm}(${reg2})"), "Lb"); } @@ -413,10 +437,18 @@ TEST_F(AssemblerMIPSTest, Lh) { DriverStr(RepeatRRIb(&mips::MipsAssembler::Lh, -16, "lh ${reg1}, {imm}(${reg2})"), "Lh"); } +TEST_F(AssemblerMIPSTest, Lwl) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Lwl, -16, "lwl ${reg1}, {imm}(${reg2})"), "Lwl"); +} + TEST_F(AssemblerMIPSTest, Lw) { DriverStr(RepeatRRIb(&mips::MipsAssembler::Lw, -16, "lw ${reg1}, {imm}(${reg2})"), "Lw"); } +TEST_F(AssemblerMIPSTest, Lwr) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Lwr, -16, "lwr ${reg1}, {imm}(${reg2})"), "Lwr"); +} + TEST_F(AssemblerMIPSTest, Lbu) { DriverStr(RepeatRRIb(&mips::MipsAssembler::Lbu, -16, "lbu ${reg1}, {imm}(${reg2})"), "Lbu"); } @@ -445,10 +477,18 @@ TEST_F(AssemblerMIPSTest, Sh) { DriverStr(RepeatRRIb(&mips::MipsAssembler::Sh, -16, "sh ${reg1}, {imm}(${reg2})"), "Sh"); } +TEST_F(AssemblerMIPSTest, Swl) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Swl, -16, "swl ${reg1}, {imm}(${reg2})"), "Swl"); +} + TEST_F(AssemblerMIPSTest, Sw) { DriverStr(RepeatRRIb(&mips::MipsAssembler::Sw, -16, "sw ${reg1}, {imm}(${reg2})"), "Sw"); } +TEST_F(AssemblerMIPSTest, Swr) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Swr, -16, "swr ${reg1}, {imm}(${reg2})"), "Swr"); +} + TEST_F(AssemblerMIPSTest, Slt) { DriverStr(RepeatRRR(&mips::MipsAssembler::Slt, "slt ${reg1}, ${reg2}, ${reg3}"), "Slt"); } diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index f9226878a9..428266fac6 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -306,13 +306,17 @@ static const MipsInstruction gMipsInstructions[] = { { kITypeMask, 32u << kOpcodeShift, "lb", "TO", }, { kITypeMask, 33u << kOpcodeShift, "lh", "TO", }, + { kITypeMask, 34u << kOpcodeShift, "lwl", "TO", }, { kITypeMask, 35u << kOpcodeShift, "lw", "TO", }, { kITypeMask, 36u << kOpcodeShift, "lbu", "TO", }, { kITypeMask, 37u << kOpcodeShift, "lhu", "TO", }, + { kITypeMask, 38u << kOpcodeShift, "lwr", "TO", }, { kITypeMask, 39u << kOpcodeShift, "lwu", "TO", }, { kITypeMask, 40u << kOpcodeShift, "sb", "TO", }, { kITypeMask, 41u << kOpcodeShift, "sh", "TO", }, + { kITypeMask, 42u << kOpcodeShift, "swl", "TO", }, { kITypeMask, 43u << kOpcodeShift, "sw", "TO", }, + { kITypeMask, 46u << kOpcodeShift, "swr", "TO", }, { kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", }, { kJTypeMask, 50u << kOpcodeShift, "bc", "P" }, { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", }, diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 188deb07a5..8d3da37762 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -294,6 +294,14 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, return false; } + if (UNLIKELY(__builtin_frame_address(0) < thread->GetStackEnd())) { + // Don't attempt to do an OSR if we are close to the stack limit. Since + // the interpreter frames are still on stack, OSR has the potential + // to stack overflow even for a simple loop. + // b/27094810. + return false; + } + // Get the actual Java method if this method is from a proxy class. The compiler // and the JIT code cache do not expect methods from proxy classes. method = method->GetInterfaceMethodIfProxy(sizeof(void*)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index fc89bfd9b0..74ff741d93 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -790,5 +790,24 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { return mspace_usable_size(reinterpret_cast<const void*>(FromCodeToAllocation(ptr))); } +void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, + const OatQuickMethodHeader* header) { + if (method->GetEntryPointFromQuickCompiledCode() == header->GetEntryPoint()) { + // The entrypoint is the one to invalidate, so we just update + // it to the interpreter entry point and clear the counter to get the method + // Jitted again. + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + method, GetQuickToInterpreterBridge()); + method->ClearCounter(); + } else { + MutexLock mu(Thread::Current(), lock_); + auto it = osr_code_map_.find(method); + if (it != osr_code_map_.end() && OatQuickMethodHeader::FromCodePointer(it->second) == header) { + // Remove the OSR method, to avoid using it again. + osr_code_map_.erase(it); + } + } +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 048f8d064e..71f5cda0c8 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -172,6 +172,10 @@ class JitCodeCache { size_t GetMemorySizeOfCodePointer(const void* ptr) REQUIRES(!lock_); + void InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* code) + REQUIRES(!lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + private: // Take ownership of maps. JitCodeCache(MemMap* code_map, diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 786cf06e2d..dd384c7586 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -23,6 +23,8 @@ #include "entrypoints/quick/quick_entrypoints_enum.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "handle_scope-inl.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/throwable.h" @@ -629,13 +631,17 @@ void QuickExceptionHandler::DeoptimizeSingleFrame() { DeoptimizeStackVisitor visitor(self_, context_, this, true); visitor.WalkStack(true); - // Compiled code made an explicit deoptimization. Transfer the code - // to interpreter and clear the counter to JIT the method again. + // Compiled code made an explicit deoptimization. ArtMethod* deopt_method = visitor.GetSingleFrameDeoptMethod(); DCHECK(deopt_method != nullptr); - deopt_method->ClearCounter(); - Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( - deopt_method, GetQuickToInterpreterBridge()); + if (Runtime::Current()->UseJit()) { + Runtime::Current()->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor( + deopt_method, handler_method_header_); + } else { + // Transfer the code to interpreter. + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + deopt_method, GetQuickToInterpreterBridge()); + } // PC needs to be of the quick-to-interpreter bridge. int32_t offset; diff --git a/runtime/utils.h b/runtime/utils.h index c00db11c16..79e4da19c8 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -111,6 +111,11 @@ template <typename T> T AbsOrMin(T value) { : std::abs(value); } +template <typename T> +inline typename std::make_unsigned<T>::type MakeUnsigned(T x) { + return static_cast<typename std::make_unsigned<T>::type>(x); +} + std::string PrintableChar(uint16_t ch); // Returns an ASCII string corresponding to the given UTF-8 string. diff --git a/test/442-checker-constant-folding/smali/TestCmp.smali b/test/442-checker-constant-folding/smali/TestCmp.smali new file mode 100644 index 0000000000..df631bc202 --- /dev/null +++ b/test/442-checker-constant-folding/smali/TestCmp.smali @@ -0,0 +1,332 @@ +# Copyright (C) 2015 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. + +.class public LTestCmp; + +.super Ljava/lang/Object; + + +## CHECK-START: int TestCmp.$opt$CmpLongConstants() constant_folding (before) +## CHECK-DAG: <<Const13:j\d+>> LongConstant 13 +## CHECK-DAG: <<Const7:j\d+>> LongConstant 7 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const13>>,<<Const7>>] +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLongConstants() constant_folding (after) +## CHECK-DAG: LongConstant 13 +## CHECK-DAG: LongConstant 7 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: Return [<<Const1>>] + +## CHECK-START: int TestCmp.$opt$CmpLongConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLongConstants()I + .registers 5 + const-wide v1, 13 + const-wide v3, 7 + cmp-long v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstants() constant_folding (before) +## CHECK-DAG: <<Const11:f\d+>> FloatConstant 11 +## CHECK-DAG: <<Const22:f\d+>> FloatConstant 22 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const11>>,<<Const22>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstants() constant_folding (after) +## CHECK-DAG: FloatConstant 11 +## CHECK-DAG: FloatConstant 22 +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtFloatConstants()I + .registers 3 + const v1, 11.f + const v2, 22.f + cmpg-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstants() constant_folding (before) +## CHECK-DAG: <<Const33:f\d+>> FloatConstant 33 +## CHECK-DAG: <<Const44:f\d+>> FloatConstant 44 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const33>>,<<Const44>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstants() constant_folding (after) +## CHECK-DAG: FloatConstant 33 +## CHECK-DAG: FloatConstant 44 +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtFloatConstants()I + .registers 3 + const v1, 33.f + const v2, 44.f + cmpl-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstants() constant_folding (before) +## CHECK-DAG: <<Const55:d\d+>> DoubleConstant 55 +## CHECK-DAG: <<Const66:d\d+>> DoubleConstant 66 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const55>>,<<Const66>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstants() constant_folding (after) +## CHECK-DAG: DoubleConstant 55 +## CHECK-DAG: DoubleConstant 66 +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtDoubleConstants()I + .registers 5 + const-wide v1, 55. + const-wide v3, 66. + cmpg-double v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstants() constant_folding (before) +## CHECK-DAG: <<Const77:d\d+>> DoubleConstant 77 +## CHECK-DAG: <<Const88:d\d+>> DoubleConstant 88 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const77>>,<<Const88>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstants() constant_folding (after) +## CHECK-DAG: DoubleConstant 77 +## CHECK-DAG: DoubleConstant 88 +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtDoubleConstants()I + .registers 5 + const-wide v1, 77. + const-wide v3, 88. + cmpl-double v0, v1, v3 + return v0 +.end method + + +## CHECK-START: int TestCmp.$opt$CmpLongSameConstant() constant_folding (before) +## CHECK-DAG: <<Const100:j\d+>> LongConstant 100 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const100>>,<<Const100>>] +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLongSameConstant() constant_folding (after) +## CHECK-DAG: LongConstant 100 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpLongSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLongSameConstant()I + .registers 5 + const-wide v1, 100 + const-wide v3, 100 + cmp-long v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtFloatSameConstant() constant_folding (before) +## CHECK-DAG: <<Const200:f\d+>> FloatConstant 200 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const200>>,<<Const200>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatSameConstant() constant_folding (after) +## CHECK-DAG: FloatConstant 200 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtFloatSameConstant()I + .registers 3 + const v1, 200.f + const v2, 200.f + cmpg-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtFloatSameConstant() constant_folding (before) +## CHECK-DAG: <<Const300:f\d+>> FloatConstant 300 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const300>>,<<Const300>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatSameConstant() constant_folding (after) +## CHECK-DAG: FloatConstant 300 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtFloatSameConstant()I + .registers 3 + const v1, 300.f + const v2, 300.f + cmpl-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleSameConstant() constant_folding (before) +## CHECK-DAG: <<Const400:d\d+>> DoubleConstant 400 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const400>>,<<Const400>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleSameConstant() constant_folding (after) +## CHECK-DAG: DoubleConstant 400 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtDoubleSameConstant()I + .registers 5 + const-wide v1, 400. + const-wide v3, 400. + cmpg-double v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleSameConstant() constant_folding (before) +## CHECK-DAG: <<Const500:d\d+>> DoubleConstant 500 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const500>>,<<Const500>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleSameConstant() constant_folding (after) +## CHECK-DAG: DoubleConstant 500 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtDoubleSameConstant()I + .registers 5 + const-wide v1, 500. + const-wide v3, 500. + cmpl-double v0, v1, v3 + return v0 +.end method + + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstantWithNaN() constant_folding (before) +## CHECK-DAG: <<Const44:f\d+>> FloatConstant 44 +## CHECK-DAG: <<ConstNan:f\d+>> FloatConstant nan +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const44>>,<<ConstNan>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstantWithNaN() constant_folding (after) +## CHECK-DAG: FloatConstant 44 +## CHECK-DAG: FloatConstant nan +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: Return [<<Const1>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstantWithNaN() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtFloatConstantWithNaN()I + .registers 3 + const v1, 44.f + const v2, NaNf + cmpg-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstantWithNaN() constant_folding (before) +## CHECK-DAG: <<Const44:f\d+>> FloatConstant 44 +## CHECK-DAG: <<ConstNan:f\d+>> FloatConstant nan +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const44>>,<<ConstNan>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstantWithNaN() constant_folding (after) +## CHECK-DAG: FloatConstant 44 +## CHECK-DAG: FloatConstant nan +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstantWithNaN() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtFloatConstantWithNaN()I + .registers 3 + const v1, 44.f + const v2, NaNf + cmpl-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstantWithNaN() constant_folding (before) +## CHECK-DAG: <<Const45:d\d+>> DoubleConstant 45 +## CHECK-DAG: <<ConstNan:d\d+>> DoubleConstant nan +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const45>>,<<ConstNan>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstantWithNaN() constant_folding (after) +## CHECK-DAG: DoubleConstant 45 +## CHECK-DAG: DoubleConstant nan +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: Return [<<Const1>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstantWithNaN() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtDoubleConstantWithNaN()I + .registers 5 + const-wide v1, 45. + const-wide v3, NaN + cmpg-double v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstantWithNaN() constant_folding (before) +## CHECK-DAG: <<Const46:d\d+>> DoubleConstant 46 +## CHECK-DAG: <<ConstNan:d\d+>> DoubleConstant nan +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const46>>,<<ConstNan>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstantWithNaN() constant_folding (after) +## CHECK-DAG: DoubleConstant 46 +## CHECK-DAG: DoubleConstant nan +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstantWithNaN() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtDoubleConstantWithNaN()I + .registers 5 + const-wide v1, 46. + const-wide v3, NaN + cmpl-double v0, v1, v3 + return v0 +.end method diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index 5479818ae7..93fe397273 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -14,8 +14,13 @@ * limitations under the License. */ +import java.lang.reflect.Method; + public class Main { + // Workaround for b/18051191. + class InnerClass {} + public static void assertFalse(boolean condition) { if (condition) { throw new Error(); @@ -47,6 +52,68 @@ public class Main { } + // Wrappers around methods located in file TestCmp.smali. + + public int smaliCmpLongConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLongConstants"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtFloatConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtFloatConstants"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtFloatConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtFloatConstants"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtDoubleConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtDoubleConstants"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtDoubleConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtDoubleConstants"); + return (Integer)m.invoke(null); + } + + public int smaliCmpLongSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLongSameConstant"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtFloatSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtFloatSameConstant"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtFloatSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtFloatSameConstant"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtDoubleSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtDoubleSameConstant"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtDoubleSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtDoubleSameConstant"); + return (Integer)m.invoke(null); + } + + public int smaliCmpGtFloatConstantWithNaN() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtFloatConstantWithNaN"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtFloatConstantWithNaN() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtFloatConstantWithNaN"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtDoubleConstantWithNaN() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtDoubleConstantWithNaN"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtDoubleConstantWithNaN() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtDoubleConstantWithNaN"); + return (Integer)m.invoke(null); + } + + /** * Exercise constant folding on negation. */ @@ -89,6 +156,44 @@ public class Main { return y; } + /// CHECK-START: float Main.FloatNegation() constant_folding (before) + /// CHECK-DAG: <<Const42:f\d+>> FloatConstant 42 + /// CHECK-DAG: <<Neg:f\d+>> Neg [<<Const42>>] + /// CHECK-DAG: Return [<<Neg>>] + + /// CHECK-START: float Main.FloatNegation() constant_folding (after) + /// CHECK-DAG: <<ConstN42:f\d+>> FloatConstant -42 + /// CHECK-DAG: Return [<<ConstN42>>] + + /// CHECK-START: float Main.FloatNegation() constant_folding (after) + /// CHECK-NOT: Neg + + public static float FloatNegation() { + float x, y; + x = 42F; + y = -x; + return y; + } + + /// CHECK-START: double Main.DoubleNegation() constant_folding (before) + /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42 + /// CHECK-DAG: <<Neg:d\d+>> Neg [<<Const42>>] + /// CHECK-DAG: Return [<<Neg>>] + + /// CHECK-START: double Main.DoubleNegation() constant_folding (after) + /// CHECK-DAG: <<ConstN42:d\d+>> DoubleConstant -42 + /// CHECK-DAG: Return [<<ConstN42>>] + + /// CHECK-START: double Main.DoubleNegation() constant_folding (after) + /// CHECK-NOT: Neg + + public static double DoubleNegation() { + double x, y; + x = 42D; + y = -x; + return y; + } + /** * Exercise constant folding on addition. @@ -166,6 +271,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatAddition() constant_folding (before) + /// CHECK-DAG: <<Const1:f\d+>> FloatConstant 1 + /// CHECK-DAG: <<Const2:f\d+>> FloatConstant 2 + /// CHECK-DAG: <<Add:f\d+>> Add [<<Const1>>,<<Const2>>] + /// CHECK-DAG: Return [<<Add>>] + + /// CHECK-START: float Main.FloatAddition() constant_folding (after) + /// CHECK-DAG: <<Const3:f\d+>> FloatConstant 3 + /// CHECK-DAG: Return [<<Const3>>] + + /// CHECK-START: float Main.FloatAddition() constant_folding (after) + /// CHECK-NOT: Add + + public static float FloatAddition() { + float a, b, c; + a = 1F; + b = 2F; + c = a + b; + return c; + } + + /// CHECK-START: double Main.DoubleAddition() constant_folding (before) + /// CHECK-DAG: <<Const1:d\d+>> DoubleConstant 1 + /// CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2 + /// CHECK-DAG: <<Add:d\d+>> Add [<<Const1>>,<<Const2>>] + /// CHECK-DAG: Return [<<Add>>] + + /// CHECK-START: double Main.DoubleAddition() constant_folding (after) + /// CHECK-DAG: <<Const3:d\d+>> DoubleConstant 3 + /// CHECK-DAG: Return [<<Const3>>] + + /// CHECK-START: double Main.DoubleAddition() constant_folding (after) + /// CHECK-NOT: Add + + public static double DoubleAddition() { + double a, b, c; + a = 1D; + b = 2D; + c = a + b; + return c; + } + /** * Exercise constant folding on subtraction. @@ -213,6 +360,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatSubtraction() constant_folding (before) + /// CHECK-DAG: <<Const6:f\d+>> FloatConstant 6 + /// CHECK-DAG: <<Const2:f\d+>> FloatConstant 2 + /// CHECK-DAG: <<Sub:f\d+>> Sub [<<Const6>>,<<Const2>>] + /// CHECK-DAG: Return [<<Sub>>] + + /// CHECK-START: float Main.FloatSubtraction() constant_folding (after) + /// CHECK-DAG: <<Const4:f\d+>> FloatConstant 4 + /// CHECK-DAG: Return [<<Const4>>] + + /// CHECK-START: float Main.FloatSubtraction() constant_folding (after) + /// CHECK-NOT: Sub + + public static float FloatSubtraction() { + float a, b, c; + a = 6F; + b = 2F; + c = a - b; + return c; + } + + /// CHECK-START: double Main.DoubleSubtraction() constant_folding (before) + /// CHECK-DAG: <<Const6:d\d+>> DoubleConstant 6 + /// CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2 + /// CHECK-DAG: <<Sub:d\d+>> Sub [<<Const6>>,<<Const2>>] + /// CHECK-DAG: Return [<<Sub>>] + + /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after) + /// CHECK-DAG: <<Const4:d\d+>> DoubleConstant 4 + /// CHECK-DAG: Return [<<Const4>>] + + /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after) + /// CHECK-NOT: Sub + + public static double DoubleSubtraction() { + double a, b, c; + a = 6D; + b = 2D; + c = a - b; + return c; + } + /** * Exercise constant folding on multiplication. @@ -260,6 +449,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatMultiplication() constant_folding (before) + /// CHECK-DAG: <<Const7:f\d+>> FloatConstant 7 + /// CHECK-DAG: <<Const3:f\d+>> FloatConstant 3 + /// CHECK-DAG: <<Mul:f\d+>> Mul [<<Const7>>,<<Const3>>] + /// CHECK-DAG: Return [<<Mul>>] + + /// CHECK-START: float Main.FloatMultiplication() constant_folding (after) + /// CHECK-DAG: <<Const21:f\d+>> FloatConstant 21 + /// CHECK-DAG: Return [<<Const21>>] + + /// CHECK-START: float Main.FloatMultiplication() constant_folding (after) + /// CHECK-NOT: Mul + + public static float FloatMultiplication() { + float a, b, c; + a = 7F; + b = 3F; + c = a * b; + return c; + } + + /// CHECK-START: double Main.DoubleMultiplication() constant_folding (before) + /// CHECK-DAG: <<Const7:d\d+>> DoubleConstant 7 + /// CHECK-DAG: <<Const3:d\d+>> DoubleConstant 3 + /// CHECK-DAG: <<Mul:d\d+>> Mul [<<Const7>>,<<Const3>>] + /// CHECK-DAG: Return [<<Mul>>] + + /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after) + /// CHECK-DAG: <<Const21:d\d+>> DoubleConstant 21 + /// CHECK-DAG: Return [<<Const21>>] + + /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after) + /// CHECK-NOT: Mul + + public static double DoubleMultiplication() { + double a, b, c; + a = 7D; + b = 3D; + c = a * b; + return c; + } + /** * Exercise constant folding on division. @@ -311,6 +542,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatDivision() constant_folding (before) + /// CHECK-DAG: <<Const8:f\d+>> FloatConstant 8 + /// CHECK-DAG: <<Const2P5:f\d+>> FloatConstant 2.5 + /// CHECK-DAG: <<Div:f\d+>> Div [<<Const8>>,<<Const2P5>>] + /// CHECK-DAG: Return [<<Div>>] + + /// CHECK-START: float Main.FloatDivision() constant_folding (after) + /// CHECK-DAG: <<Const3P2:f\d+>> FloatConstant 3.2 + /// CHECK-DAG: Return [<<Const3P2>>] + + /// CHECK-START: float Main.FloatDivision() constant_folding (after) + /// CHECK-NOT: Div + + public static float FloatDivision() { + float a, b, c; + a = 8F; + b = 2.5F; + c = a / b; + return c; + } + + /// CHECK-START: double Main.DoubleDivision() constant_folding (before) + /// CHECK-DAG: <<Const8:d\d+>> DoubleConstant 8 + /// CHECK-DAG: <<Const2P5:d\d+>> DoubleConstant 2.5 + /// CHECK-DAG: <<Div:d\d+>> Div [<<Const8>>,<<Const2P5>>] + /// CHECK-DAG: Return [<<Div>>] + + /// CHECK-START: double Main.DoubleDivision() constant_folding (after) + /// CHECK-DAG: <<Const3P2:d\d+>> DoubleConstant 3.2 + /// CHECK-DAG: Return [<<Const3P2>>] + + /// CHECK-START: double Main.DoubleDivision() constant_folding (after) + /// CHECK-NOT: Div + + public static double DoubleDivision() { + double a, b, c; + a = 8D; + b = 2.5D; + c = a / b; + return c; + } + /** * Exercise constant folding on remainder. @@ -362,6 +635,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatRemainder() constant_folding (before) + /// CHECK-DAG: <<Const8:f\d+>> FloatConstant 8 + /// CHECK-DAG: <<Const2P5:f\d+>> FloatConstant 2.5 + /// CHECK-DAG: <<Rem:f\d+>> Rem [<<Const8>>,<<Const2P5>>] + /// CHECK-DAG: Return [<<Rem>>] + + /// CHECK-START: float Main.FloatRemainder() constant_folding (after) + /// CHECK-DAG: <<Const0P5:f\d+>> FloatConstant 0.5 + /// CHECK-DAG: Return [<<Const0P5>>] + + /// CHECK-START: float Main.FloatRemainder() constant_folding (after) + /// CHECK-NOT: Rem + + public static float FloatRemainder() { + float a, b, c; + a = 8F; + b = 2.5F; + c = a % b; + return c; + } + + /// CHECK-START: double Main.DoubleRemainder() constant_folding (before) + /// CHECK-DAG: <<Const8:d\d+>> DoubleConstant 8 + /// CHECK-DAG: <<Const2P5:d\d+>> DoubleConstant 2.5 + /// CHECK-DAG: <<Rem:d\d+>> Rem [<<Const8>>,<<Const2P5>>] + /// CHECK-DAG: Return [<<Rem>>] + + /// CHECK-START: double Main.DoubleRemainder() constant_folding (after) + /// CHECK-DAG: <<Const0P5:d\d+>> DoubleConstant 0.5 + /// CHECK-DAG: Return [<<Const0P5>>] + + /// CHECK-START: double Main.DoubleRemainder() constant_folding (after) + /// CHECK-NOT: Rem + + public static double DoubleRemainder() { + double a, b, c; + a = 8D; + b = 2.5D; + c = a % b; + return c; + } + /** * Exercise constant folding on left shift. @@ -1197,25 +1512,37 @@ public class Main { } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { assertIntEquals(-42, IntNegation()); assertLongEquals(-42L, LongNegation()); + assertFloatEquals(-42F, FloatNegation()); + assertDoubleEquals(-42D, DoubleNegation()); assertIntEquals(3, IntAddition1()); assertIntEquals(14, IntAddition2()); assertLongEquals(3L, LongAddition()); + assertFloatEquals(3F, FloatAddition()); + assertDoubleEquals(3D, DoubleAddition()); assertIntEquals(4, IntSubtraction()); assertLongEquals(4L, LongSubtraction()); + assertFloatEquals(4F, FloatSubtraction()); + assertDoubleEquals(4D, DoubleSubtraction()); assertIntEquals(21, IntMultiplication()); assertLongEquals(21L, LongMultiplication()); + assertFloatEquals(21F, FloatMultiplication()); + assertDoubleEquals(21D, DoubleMultiplication()); assertIntEquals(2, IntDivision()); assertLongEquals(2L, LongDivision()); + assertFloatEquals(3.2F, FloatDivision()); + assertDoubleEquals(3.2D, DoubleDivision()); assertIntEquals(2, IntRemainder()); assertLongEquals(2L, LongRemainder()); + assertFloatEquals(0.5F, FloatRemainder()); + assertDoubleEquals(0.5D, DoubleRemainder()); assertIntEquals(4, ShlIntLong()); assertLongEquals(12L, ShlLongInt()); @@ -1259,6 +1586,24 @@ public class Main { assertFalse(CmpFloatGreaterThanNaN(arbitrary)); assertFalse(CmpDoubleLessThanNaN(arbitrary)); + Main main = new Main(); + assertIntEquals(1, main.smaliCmpLongConstants()); + assertIntEquals(-1, main.smaliCmpGtFloatConstants()); + assertIntEquals(-1, main.smaliCmpLtFloatConstants()); + assertIntEquals(-1, main.smaliCmpGtDoubleConstants()); + assertIntEquals(-1, main.smaliCmpLtDoubleConstants()); + + assertIntEquals(0, main.smaliCmpLongSameConstant()); + assertIntEquals(0, main.smaliCmpGtFloatSameConstant()); + assertIntEquals(0, main.smaliCmpLtFloatSameConstant()); + assertIntEquals(0, main.smaliCmpGtDoubleSameConstant()); + assertIntEquals(0, main.smaliCmpLtDoubleSameConstant()); + + assertIntEquals(1, main.smaliCmpGtFloatConstantWithNaN()); + assertIntEquals(-1, main.smaliCmpLtFloatConstantWithNaN()); + assertIntEquals(1, main.smaliCmpGtDoubleConstantWithNaN()); + assertIntEquals(-1, main.smaliCmpLtDoubleConstantWithNaN()); + assertIntEquals(33, ReturnInt33()); assertIntEquals(2147483647, ReturnIntMax()); assertIntEquals(0, ReturnInt0()); @@ -1275,4 +1620,10 @@ public class Main { assertDoubleEquals(34, ReturnDouble34()); assertDoubleEquals(99.25, ReturnDouble99P25()); } + + Main() throws ClassNotFoundException { + testCmp = Class.forName("TestCmp"); + } + + private Class<?> testCmp; } diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc index 4c58b39319..09e97ea751 100644 --- a/test/570-checker-osr/osr.cc +++ b/test/570-checker-osr/osr.cc @@ -41,7 +41,8 @@ class OsrVisitor : public StackVisitor { (m_name.compare("$noinline$returnDouble") == 0) || (m_name.compare("$noinline$returnLong") == 0) || (m_name.compare("$noinline$deopt") == 0) || - (m_name.compare("$noinline$inlineCache") == 0)) { + (m_name.compare("$noinline$inlineCache") == 0) || + (m_name.compare("$noinline$stackOverflow") == 0)) { const OatQuickMethodHeader* header = Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m); if (header != nullptr && header == GetCurrentOatQuickMethodHeader()) { @@ -91,7 +92,8 @@ class ProfilingInfoVisitor : public StackVisitor { ArtMethod* m = GetMethod(); std::string m_name(m->GetName()); - if (m_name.compare("$noinline$inlineCache") == 0) { + if ((m_name.compare("$noinline$inlineCache") == 0) || + (m_name.compare("$noinline$stackOverflow") == 0)) { ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true); return false; } @@ -119,7 +121,8 @@ class OsrCheckVisitor : public StackVisitor { std::string m_name(m->GetName()); jit::Jit* jit = Runtime::Current()->GetJit(); - if (m_name.compare("$noinline$inlineCache") == 0 && jit != nullptr) { + if ((m_name.compare("$noinline$inlineCache") == 0) || + (m_name.compare("$noinline$stackOverflow") == 0)) { while (jit->GetCodeCache()->LookupOsrMethodHeader(m) == nullptr) { // Sleep to yield to the compiler thread. sleep(0); diff --git a/test/570-checker-osr/run b/test/570-checker-osr/run new file mode 100755 index 0000000000..24d69b4b3b --- /dev/null +++ b/test/570-checker-osr/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2016 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. + +# Ensure this test is not subject to code collection. +exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java index 828908a582..1142d49eed 100644 --- a/test/570-checker-osr/src/Main.java +++ b/test/570-checker-osr/src/Main.java @@ -40,6 +40,9 @@ public class Main { if ($noinline$inlineCache(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) { throw new Error("Unexpected return value"); } + + $noinline$stackOverflow(new Main(), /* isSecondInvocation */ false); + $noinline$stackOverflow(new SubMain(), /* isSecondInvocation */ true); } public static int $noinline$returnInt() { @@ -129,7 +132,32 @@ public class Main { return Main.class; } - public static int[] array = new int[4]; + public void otherInlineCache() { + return; + } + + public static void $noinline$stackOverflow(Main m, boolean isSecondInvocation) { + // If we are running in non-JIT mode, or were unlucky enough to get this method + // already JITted, just return the expected value. + if (!ensureInInterpreter()) { + return; + } + + // We need a ProfilingInfo object to populate the 'otherInlineCache' call. + ensureHasProfilingInfo(); + + if (isSecondInvocation) { + // Ensure we have an OSR code and we jump to it. + while (!ensureInOsrCode()) {} + } + + for (int i = 0; i < (isSecondInvocation ? 10000000 : 1); ++i) { + // The first invocation of $noinline$stackOverflow will populate the inline + // cache with Main. The second invocation of the method, will see a SubMain + // and will therefore trigger deoptimization. + m.otherInlineCache(); + } + } public static native boolean ensureInInterpreter(); public static native boolean ensureInOsrCode(); @@ -147,4 +175,8 @@ class SubMain extends Main { public Main inlineCache() { return new SubMain(); } + + public void otherInlineCache() { + return; + } } diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index f29e51f047..e4af9fa0d7 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -20,9 +20,9 @@ if [ ! -d libcore ]; then fi # Jar containing all the tests. -test_jar=${OUT_DIR-out}/host/linux-x86/framework/apache-harmony-jdwp-tests-hostdex.jar +test_jack=${OUT_DIR-out}/host/common/obj/JAVA_LIBRARIES/apache-harmony-jdwp-tests-hostdex_intermediates/classes.jack -if [ ! -f $test_jar ]; then +if [ ! -f $test_jack ]; then echo "Before running, you must build jdwp tests and vogar:" \ "make apache-harmony-jdwp-tests-hostdex vogar vogar.jar" exit 1 @@ -117,6 +117,9 @@ if [[ $verbose == "yes" ]]; then art_debugee="$art_debugee -verbose:jdwp" fi +# Use Jack with "1.8" configuration. +export JACK_VERSION=`basename prebuilts/sdk/tools/jacks/*ALPHA* | sed 's/^jack-//' | sed 's/.jar$//'` + # Run the tests using vogar. vogar $vm_command \ $vm_args \ @@ -129,7 +132,8 @@ vogar $vm_command \ --vm-arg -Djpda.settings.syncPort=34016 \ --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \ --vm-arg -Djpda.settings.debuggeeJavaPath="$art_debugee $image $debuggee_args" \ - --classpath $test_jar \ + --classpath $test_jack \ + --toolchain jack --language JN \ --vm-arg -Xcompiler-option --vm-arg --debuggable \ $test |