diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/optimizing/code_generator.cc | 34 | ||||
-rw-r--r-- | compiler/optimizing/code_generator.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 68 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.h | 3 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 37 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 53 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 29 | ||||
-rw-r--r-- | compiler/optimizing/nodes.cc | 23 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 55 |
10 files changed, 246 insertions, 60 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index cdfd989bb8..8d9a7b71e9 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -638,6 +638,40 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { } } +bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) { + HInstruction* first_next_not_move = null_check->GetNextDisregardingMoves(); + return (first_next_not_move != nullptr) && first_next_not_move->CanDoImplicitNullCheck(); +} + +void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) { + // If we are from a static path don't record the pc as we can't throw NPE. + // NB: having the checks here makes the code much less verbose in the arch + // specific code generators. + if (instr->IsStaticFieldSet() || instr->IsStaticFieldGet()) { + return; + } + + if (!compiler_options_.GetImplicitNullChecks()) { + return; + } + + if (!instr->CanDoImplicitNullCheck()) { + return; + } + + // Find the first previous instruction which is not a move. + HInstruction* first_prev_not_move = instr->GetPreviousDisregardingMoves(); + + // If the instruction is a null check it means that `instr` is the first user + // and needs to record the pc. + if (first_prev_not_move != nullptr && first_prev_not_move->IsNullCheck()) { + HNullCheck* null_check = first_prev_not_move->AsNullCheck(); + // TODO: The parallel moves modify the environment. Their changes need to be reverted + // otherwise the stack maps at the throw point will not be correct. + RecordPcInfo(null_check, null_check->GetDexPc()); + } +} + void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) { RegisterSet* register_set = locations->GetLiveRegisters(); size_t stack_offset = first_register_slot_in_slow_path_; diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 682c260585..f66aed912a 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -152,6 +152,8 @@ class CodeGenerator { virtual bool NeedsTwoRegisters(Primitive::Type type) const = 0; void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc); + bool CanMoveNullCheckToUser(HNullCheck* null_check); + void MaybeRecordImplicitNullCheck(HInstruction* instruction); void AddSlowPath(SlowPathCode* slow_path) { slow_paths_.Add(slow_path); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 07c84bcc01..980de040fa 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1226,6 +1226,7 @@ void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { } else { __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); } + codegen_->MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetMethodAt(method_offset); uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( kArmWordSize).Int32Value(); @@ -1264,6 +1265,7 @@ void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) } else { __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); } + codegen_->MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetImtEntryAt(method_offset); uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( kArmWordSize).Int32Value(); @@ -2579,7 +2581,8 @@ void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, Register value_lo, Register value_hi, Register temp1, - Register temp2) { + Register temp2, + HInstruction* instruction) { Label fail; if (offset != 0) { __ LoadImmediate(temp1, offset); @@ -2590,6 +2593,7 @@ void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, // We need a load followed by store. (The address used in a STREX instruction must // be the same as the address in the most recently executed LDREX instruction.) __ ldrexd(temp1, temp2, addr); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ strexd(temp1, value_lo, value_hi, addr); __ cmp(temp1, ShifterOperand(0)); __ b(&fail, NE); @@ -2664,13 +2668,7 @@ void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, case Primitive::kPrimInt: case Primitive::kPrimNot: { - Register value_reg = value.AsRegister<Register>(); - __ StoreToOffset(kStoreWord, value_reg, base, offset); - if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { - Register temp = locations->GetTemp(0).AsRegister<Register>(); - Register card = locations->GetTemp(1).AsRegister<Register>(); - codegen_->MarkGCCard(temp, card, base, value_reg); - } + __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset); break; } @@ -2680,9 +2678,11 @@ void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, value.AsRegisterPairLow<Register>(), value.AsRegisterPairHigh<Register>(), locations->GetTemp(0).AsRegister<Register>(), - locations->GetTemp(1).AsRegister<Register>()); + locations->GetTemp(1).AsRegister<Register>(), + instruction); } else { __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset); + codegen_->MaybeRecordImplicitNullCheck(instruction); } break; } @@ -2704,9 +2704,11 @@ void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, value_reg_lo, value_reg_hi, locations->GetTemp(2).AsRegister<Register>(), - locations->GetTemp(3).AsRegister<Register>()); + locations->GetTemp(3).AsRegister<Register>(), + instruction); } else { __ StoreDToOffset(value_reg, base, offset); + codegen_->MaybeRecordImplicitNullCheck(instruction); } break; } @@ -2716,6 +2718,17 @@ void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, UNREACHABLE(); } + // Longs and doubles are handled in the switch. + if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } + + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { + Register temp = locations->GetTemp(0).AsRegister<Register>(); + Register card = locations->GetTemp(1).AsRegister<Register>(); + codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>()); + } + if (is_volatile) { GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } @@ -2804,9 +2817,11 @@ void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, Register lo = locations->GetTemp(0).AsRegister<Register>(); Register hi = locations->GetTemp(1).AsRegister<Register>(); GenerateWideAtomicLoad(base, offset, lo, hi); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ vmovdrr(out_reg, lo, hi); } else { __ LoadDFromOffset(out_reg, base, offset); + codegen_->MaybeRecordImplicitNullCheck(instruction); } break; } @@ -2816,6 +2831,11 @@ void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, UNREACHABLE(); } + // Doubles are handled in the switch. + if (field_type != Primitive::kPrimDouble) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } + if (is_volatile) { GenerateMemoryBarrier(MemBarrierKind::kLoadAny); } @@ -2856,17 +2876,18 @@ void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instructi void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks() - ? Location::RequiresRegister() - : Location::RegisterOrConstant(instruction->InputAt(0)); - locations->SetInAt(0, loc); + locations->SetInAt(0, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); } } void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (codegen_->CanMoveNullCheckToUser(instruction)) { + return; + } Location obj = instruction->GetLocations()->InAt(0); + __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0); codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } @@ -2878,14 +2899,8 @@ void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruct LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); - if (obj.IsRegister()) { - __ cmp(obj.AsRegister<Register>(), ShifterOperand(0)); - __ b(slow_path->GetEntryLabel(), EQ); - } else { - DCHECK(obj.IsConstant()) << obj; - DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); - __ b(slow_path->GetEntryLabel()); - } + __ cmp(obj.AsRegister<Register>(), ShifterOperand(0)); + __ b(slow_path->GetEntryLabel(), EQ); } void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { @@ -3028,6 +3043,7 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); } + codegen_->MaybeRecordImplicitNullCheck(instruction); } void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { @@ -3111,6 +3127,7 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); __ StoreToOffset(kStoreWord, value, IP, data_offset); } + codegen_->MaybeRecordImplicitNullCheck(instruction); if (needs_write_barrier) { DCHECK_EQ(value_type, Primitive::kPrimNot); Register temp = locations->GetTemp(0).AsRegister<Register>(); @@ -3165,6 +3182,7 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); } + break; } @@ -3172,6 +3190,11 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { LOG(FATAL) << "Unreachable type " << value_type; UNREACHABLE(); } + + // Ints and objects are handled in the switch. + if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } } void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { @@ -3187,6 +3210,7 @@ void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { Register obj = locations->InAt(0).AsRegister<Register>(); Register out = locations->Out().AsRegister<Register>(); __ LoadFromOffset(kLoadWord, out, obj, offset); + codegen_->MaybeRecordImplicitNullCheck(instruction); } void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index fe373d54e3..0de6669aa7 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -139,7 +139,8 @@ class InstructionCodeGeneratorARM : public HGraphVisitor { void GenerateMemoryBarrier(MemBarrierKind kind); void GenerateWideAtomicStore(Register addr, uint32_t offset, Register value_lo, Register value_hi, - Register temp1, Register temp2); + Register temp1, Register temp2, + HInstruction* instruction); void GenerateWideAtomicLoad(Register addr, uint32_t offset, Register out_lo, Register out_hi); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 306845beb8..0109363062 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -997,11 +997,12 @@ void CodeGeneratorARM64::Load(Primitive::Type type, } } -void CodeGeneratorARM64::LoadAcquire(Primitive::Type type, +void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction, CPURegister dst, const MemOperand& src) { UseScratchRegisterScope temps(GetVIXLAssembler()); Register temp_base = temps.AcquireX(); + Primitive::Type type = instruction->GetType(); DCHECK(!src.IsRegisterOffset()); DCHECK(!src.IsPreIndex()); @@ -1013,16 +1014,20 @@ void CodeGeneratorARM64::LoadAcquire(Primitive::Type type, switch (type) { case Primitive::kPrimBoolean: __ Ldarb(Register(dst), base); + MaybeRecordImplicitNullCheck(instruction); break; case Primitive::kPrimByte: __ Ldarb(Register(dst), base); + MaybeRecordImplicitNullCheck(instruction); __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); break; case Primitive::kPrimChar: __ Ldarh(Register(dst), base); + MaybeRecordImplicitNullCheck(instruction); break; case Primitive::kPrimShort: __ Ldarh(Register(dst), base); + MaybeRecordImplicitNullCheck(instruction); __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); break; case Primitive::kPrimInt: @@ -1030,6 +1035,7 @@ void CodeGeneratorARM64::LoadAcquire(Primitive::Type type, case Primitive::kPrimLong: DCHECK_EQ(dst.Is64Bits(), Is64BitType(type)); __ Ldar(Register(dst), base); + MaybeRecordImplicitNullCheck(instruction); break; case Primitive::kPrimFloat: case Primitive::kPrimDouble: { @@ -1038,6 +1044,7 @@ void CodeGeneratorARM64::LoadAcquire(Primitive::Type type, Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW(); __ Ldar(temp, base); + MaybeRecordImplicitNullCheck(instruction); __ Fmov(FPRegister(dst), temp); break; } @@ -1399,6 +1406,7 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { } codegen_->Load(type, OutputCPURegister(instruction), source); + codegen_->MaybeRecordImplicitNullCheck(instruction); } void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) { @@ -1410,6 +1418,7 @@ void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) { void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) { __ Ldr(OutputRegister(instruction), HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset())); + codegen_->MaybeRecordImplicitNullCheck(instruction); } void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) { @@ -1454,6 +1463,7 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { } codegen_->Store(value_type, value, destination); + codegen_->MaybeRecordImplicitNullCheck(instruction); } } @@ -1816,14 +1826,17 @@ void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* ins if (instruction->IsVolatile()) { if (kUseAcquireRelease) { - codegen_->LoadAcquire(instruction->GetType(), OutputCPURegister(instruction), field); + // NB: LoadAcquire will record the pc info if needed. + codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field); } else { codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field); + codegen_->MaybeRecordImplicitNullCheck(instruction); // For IRIW sequential consistency kLoadAny is not sufficient. GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } } else { codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field); + codegen_->MaybeRecordImplicitNullCheck(instruction); } } @@ -1843,13 +1856,16 @@ void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* ins if (instruction->IsVolatile()) { if (kUseAcquireRelease) { codegen_->StoreRelease(field_type, value, HeapOperand(obj, offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); } else { GenerateMemoryBarrier(MemBarrierKind::kAnyStore); codegen_->Store(field_type, value, HeapOperand(obj, offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } } else { codegen_->Store(field_type, value, HeapOperand(obj, offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); } if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { @@ -1953,6 +1969,7 @@ void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invok } else { __ Ldr(temp, HeapOperandFrom(receiver, class_offset)); } + codegen_->MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetImtEntryAt(method_offset); __ Ldr(temp, HeapOperand(temp, method_offset)); // lr = temp->GetEntryPoint(); @@ -2018,6 +2035,7 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { DCHECK(receiver.IsRegister()); __ Ldr(temp, HeapOperandFrom(receiver, class_offset)); } + codegen_->MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetMethodAt(method_offset); __ Ldr(temp, HeapOperand(temp, method_offset)); // lr = temp->GetEntryPoint(); @@ -2293,6 +2311,9 @@ void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (codegen_->CanMoveNullCheckToUser(instruction)) { + return; + } Location obj = instruction->GetLocations()->InAt(0); __ Ldr(wzr, HeapOperandFrom(obj, Offset(0))); @@ -2305,13 +2326,8 @@ void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instru LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); - if (obj.IsRegister()) { - __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel()); - } else { - DCHECK(obj.IsConstant()) << obj; - DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); - __ B(slow_path->GetEntryLabel()); - } + + __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel()); } void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) { @@ -2512,7 +2528,8 @@ void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruc if (instruction->IsVolatile()) { if (kUseAcquireRelease) { - codegen_->LoadAcquire(instruction->GetType(), OutputCPURegister(instruction), field); + // NB: LoadAcquire will record the pc info if needed. + codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field); } else { codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field); // For IRIW sequential consistency kLoadAny is not sufficient. diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index e20d02ee2d..27c6fbdbf4 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -267,7 +267,7 @@ class CodeGeneratorARM64 : public CodeGenerator { void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src); void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst); void LoadCurrentMethod(vixl::Register current_method); - void LoadAcquire(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src); + void LoadAcquire(HInstruction* instruction, vixl::CPURegister dst, const vixl::MemOperand& src); void StoreRelease(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst); // Generate code to invoke a runtime entry point. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 57f01e8e16..ac6fdbcfe9 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1202,6 +1202,7 @@ void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { } else { __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset)); } + codegen_->MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetMethodAt(method_offset); __ movl(temp, Address(temp, method_offset)); // call temp->GetEntryPoint(); @@ -1238,6 +1239,7 @@ void InstructionCodeGeneratorX86::VisitInvokeInterface(HInvokeInterface* invoke) } else { __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset)); } + codegen_->MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetImtEntryAt(method_offset); __ movl(temp, Address(temp, method_offset)); // call temp->GetEntryPoint(); @@ -2808,11 +2810,13 @@ void InstructionCodeGeneratorX86::HandleFieldGet(HInstruction* instruction, if (is_volatile) { XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); __ movsd(temp, Address(base, offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movd(out.AsRegisterPairLow<Register>(), temp); __ psrlq(temp, Immediate(32)); __ movd(out.AsRegisterPairHigh<Register>(), temp); } else { __ movl(out.AsRegisterPairLow<Register>(), Address(base, offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movl(out.AsRegisterPairHigh<Register>(), Address(base, kX86WordSize + offset)); } break; @@ -2833,6 +2837,11 @@ void InstructionCodeGeneratorX86::HandleFieldGet(HInstruction* instruction, UNREACHABLE(); } + // Longs are handled in the switch. + if (field_type != Primitive::kPrimLong) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } + if (is_volatile) { GenerateMemoryBarrier(MemBarrierKind::kLoadAny); } @@ -2904,12 +2913,6 @@ void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction, case Primitive::kPrimInt: case Primitive::kPrimNot: { __ movl(Address(base, offset), value.AsRegister<Register>()); - - if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { - Register temp = locations->GetTemp(0).AsRegister<Register>(); - Register card = locations->GetTemp(1).AsRegister<Register>(); - codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>()); - } break; } @@ -2921,8 +2924,10 @@ void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction, __ movd(temp2, value.AsRegisterPairHigh<Register>()); __ punpckldq(temp1, temp2); __ movsd(Address(base, offset), temp1); + codegen_->MaybeRecordImplicitNullCheck(instruction); } else { __ movl(Address(base, offset), value.AsRegisterPairLow<Register>()); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movl(Address(base, kX86WordSize + offset), value.AsRegisterPairHigh<Register>()); } break; @@ -2943,6 +2948,17 @@ void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction, UNREACHABLE(); } + // Longs are handled in the switch. + if (field_type != Primitive::kPrimLong) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } + + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { + Register temp = locations->GetTemp(0).AsRegister<Register>(); + Register card = locations->GetTemp(1).AsRegister<Register>(); + codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>()); + } + if (is_volatile) { GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } @@ -2993,8 +3009,12 @@ void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (codegen_->CanMoveNullCheckToUser(instruction)) { + return; + } LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); + __ testl(EAX, Address(obj.AsRegister<Register>(), 0)); codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } @@ -3040,7 +3060,8 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { Register obj = locations->InAt(0).AsRegister<Register>(); Location index = locations->InAt(1); - switch (instruction->GetType()) { + Primitive::Type type = instruction->GetType(); + switch (type) { case Primitive::kPrimBoolean: { uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); @@ -3108,10 +3129,12 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; __ movl(out.AsRegisterPairLow<Register>(), Address(obj, offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movl(out.AsRegisterPairHigh<Register>(), Address(obj, offset + kX86WordSize)); } else { __ movl(out.AsRegisterPairLow<Register>(), Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movl(out.AsRegisterPairHigh<Register>(), Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize)); } @@ -3120,12 +3143,16 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimFloat: case Primitive::kPrimDouble: - LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + LOG(FATAL) << "Unimplemented register type " << type; UNREACHABLE(); case Primitive::kPrimVoid: - LOG(FATAL) << "Unreachable type " << instruction->GetType(); + LOG(FATAL) << "Unreachable type " << type; UNREACHABLE(); } + + if (type != Primitive::kPrimLong) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } } void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) { @@ -3202,6 +3229,7 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } } + codegen_->MaybeRecordImplicitNullCheck(instruction); break; } @@ -3225,6 +3253,7 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } } + codegen_->MaybeRecordImplicitNullCheck(instruction); break; } @@ -3253,6 +3282,7 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } } + codegen_->MaybeRecordImplicitNullCheck(instruction); if (needs_write_barrier) { Register temp = locations->GetTemp(0).AsRegister<Register>(); @@ -3274,17 +3304,20 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; if (value.IsRegisterPair()) { __ movl(Address(obj, offset), value.AsRegisterPairLow<Register>()); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh<Register>()); } else { DCHECK(value.IsConstant()); int64_t val = value.GetConstant()->AsLongConstant()->GetValue(); __ movl(Address(obj, offset), Immediate(Low32Bits(val))); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movl(Address(obj, offset + kX86WordSize), Immediate(High32Bits(val))); } } else { if (value.IsRegisterPair()) { __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset), value.AsRegisterPairLow<Register>()); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize), value.AsRegisterPairHigh<Register>()); } else { @@ -3292,6 +3325,7 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { int64_t val = value.GetConstant()->AsLongConstant()->GetValue(); __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset), Immediate(Low32Bits(val))); + codegen_->MaybeRecordImplicitNullCheck(instruction); __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize), Immediate(High32Bits(val))); } @@ -3322,6 +3356,7 @@ void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) { Register obj = locations->InAt(0).AsRegister<Register>(); Register out = locations->Out().AsRegister<Register>(); __ movl(out, Address(obj, offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); } void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index dd6861f67b..350392fbf4 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1244,6 +1244,7 @@ void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) } else { __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset)); } + codegen_->MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetMethodAt(method_offset); __ movl(temp, Address(temp, method_offset)); // call temp->GetEntryPoint(); @@ -1280,6 +1281,7 @@ void InstructionCodeGeneratorX86_64::VisitInvokeInterface(HInvokeInterface* invo } else { __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset)); } + codegen_->MaybeRecordImplicitNullCheck(invoke); // temp = temp->GetImtEntryAt(method_offset); __ movl(temp, Address(temp, method_offset)); // call temp->GetEntryPoint(); @@ -2589,6 +2591,8 @@ void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction, UNREACHABLE(); } + codegen_->MaybeRecordImplicitNullCheck(instruction); + if (is_volatile) { GenerateMemoryBarrier(MemBarrierKind::kLoadAny); } @@ -2643,11 +2647,6 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction, case Primitive::kPrimInt: case Primitive::kPrimNot: { __ movl(Address(base, offset), value.AsRegister<CpuRegister>()); - if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { - CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>(); - CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>(); - codegen_->MarkGCCard(temp, card, base, value.AsRegister<CpuRegister>()); - } break; } @@ -2671,6 +2670,14 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction, UNREACHABLE(); } + codegen_->MaybeRecordImplicitNullCheck(instruction); + + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { + CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>(); + CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>(); + codegen_->MarkGCCard(temp, card, base, value.AsRegister<CpuRegister>()); + } + if (is_volatile) { GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } @@ -2721,6 +2728,9 @@ void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (codegen_->CanMoveNullCheckToUser(instruction)) { + return; + } LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); @@ -2873,6 +2883,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); } + codegen_->MaybeRecordImplicitNullCheck(instruction); } void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) { @@ -2941,6 +2952,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } } + codegen_->MaybeRecordImplicitNullCheck(instruction); break; } @@ -2967,6 +2979,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } } + codegen_->MaybeRecordImplicitNullCheck(instruction); break; } @@ -2995,7 +3008,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } } - + codegen_->MaybeRecordImplicitNullCheck(instruction); if (needs_write_barrier) { DCHECK_EQ(value_type, Primitive::kPrimNot); CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>(); @@ -3023,6 +3036,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { __ movq(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset), value.AsRegister<CpuRegister>()); } + codegen_->MaybeRecordImplicitNullCheck(instruction); break; } @@ -3037,6 +3051,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { __ movss(Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset), value.AsFpuRegister<XmmRegister>()); } + codegen_->MaybeRecordImplicitNullCheck(instruction); break; } @@ -3051,6 +3066,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { __ movsd(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset), value.AsFpuRegister<XmmRegister>()); } + codegen_->MaybeRecordImplicitNullCheck(instruction); break; } @@ -3073,6 +3089,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>(); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); __ movl(out, Address(obj, offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); } void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index ade31380ec..39ec22bea1 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -15,6 +15,7 @@ */ #include "nodes.h" + #include "ssa_builder.h" #include "utils/growable_array.h" @@ -456,6 +457,22 @@ static void RemoveFromUseList(T* user, } } +HInstruction* HInstruction::GetNextDisregardingMoves() const { + HInstruction* next = GetNext(); + while (next != nullptr && next->IsParallelMove()) { + next = next->GetNext(); + } + return next; +} + +HInstruction* HInstruction::GetPreviousDisregardingMoves() const { + HInstruction* previous = GetPrevious(); + while (previous != nullptr && previous->IsParallelMove()) { + previous = previous->GetPrevious(); + } + return previous; +} + void HInstruction::RemoveUser(HInstruction* user, size_t input_index) { RemoveFromUseList(user, input_index, &uses_); } @@ -654,11 +671,7 @@ HConstant* HBinaryOperation::TryStaticEvaluation() const { } bool HCondition::IsBeforeWhenDisregardMoves(HIf* if_) const { - HInstruction* previous = if_->GetPrevious(); - while (previous != nullptr && previous->IsParallelMove()) { - previous = previous->GetPrevious(); - } - return previous == this; + return this == if_->GetPreviousDisregardingMoves(); } bool HInstruction::Equals(HInstruction* other) const { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index fa51f27f0a..0bd571a1fb 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -696,6 +696,9 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { HInstruction* GetNext() const { return next_; } HInstruction* GetPrevious() const { return previous_; } + HInstruction* GetNextDisregardingMoves() const; + HInstruction* GetPreviousDisregardingMoves() const; + HBasicBlock* GetBlock() const { return block_; } void SetBlock(HBasicBlock* block) { block_ = block; } bool IsInBlock() const { return block_ != nullptr; } @@ -716,6 +719,8 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { virtual bool CanThrow() const { return false; } bool HasSideEffects() const { return side_effects_.HasSideEffects(); } + virtual bool CanDoImplicitNullCheck() const { return false; } + void AddUseAt(HInstruction* user, size_t index) { uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HInstruction>(user, index, uses_); } @@ -1597,7 +1602,7 @@ class HInvoke : public HInstruction { // Runtime needs to walk the stack, so Dex -> Dex calls need to // know their environment. - virtual bool NeedsEnvironment() const { return true; } + bool NeedsEnvironment() const OVERRIDE { return true; } void SetArgumentAt(size_t index, HInstruction* argument) { SetRawInputAt(index, argument); @@ -1659,6 +1664,12 @@ class HInvokeStaticOrDirect : public HInvoke { : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), invoke_type_(invoke_type) {} + bool CanDoImplicitNullCheck() const OVERRIDE { + // We access the method via the dex cache so we can't do an implicit null check. + // TODO: for intrinsics we can generate implicit null checks. + return false; + } + InvokeType GetInvokeType() const { return invoke_type_; } DECLARE_INSTRUCTION(InvokeStaticOrDirect); @@ -1680,6 +1691,11 @@ class HInvokeVirtual : public HInvoke { : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), vtable_index_(vtable_index) {} + bool CanDoImplicitNullCheck() const OVERRIDE { + // TODO: Add implicit null checks in intrinsics. + return !GetLocations()->Intrinsified(); + } + uint32_t GetVTableIndex() const { return vtable_index_; } DECLARE_INSTRUCTION(InvokeVirtual); @@ -1701,6 +1717,11 @@ class HInvokeInterface : public HInvoke { : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), imt_index_(imt_index) {} + bool CanDoImplicitNullCheck() const OVERRIDE { + // TODO: Add implicit null checks in intrinsics. + return !GetLocations()->Intrinsified(); + } + uint32_t GetImtIndex() const { return imt_index_; } uint32_t GetDexMethodIndex() const { return dex_method_index_; } @@ -2180,7 +2201,11 @@ class HInstanceFieldGet : public HExpression<1> { return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue(); } - virtual size_t ComputeHashCode() const { + bool CanDoImplicitNullCheck() const OVERRIDE { + return GetFieldOffset().Uint32Value() < kPageSize; + } + + size_t ComputeHashCode() const OVERRIDE { return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue(); } @@ -2210,11 +2235,14 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { SetRawInputAt(1, value); } + bool CanDoImplicitNullCheck() const OVERRIDE { + return GetFieldOffset().Uint32Value() < kPageSize; + } + const FieldInfo& GetFieldInfo() const { return field_info_; } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } - HInstruction* GetValue() const { return InputAt(1); } DECLARE_INSTRUCTION(InstanceFieldSet); @@ -2238,6 +2266,15 @@ class HArrayGet : public HExpression<2> { UNUSED(other); return true; } + bool CanDoImplicitNullCheck() const OVERRIDE { + // TODO: We can be smarter here. + // Currently, the array access is always preceded by an ArrayLength or a NullCheck + // which generates the implicit null check. There are cases when these can be removed + // to produce better code. If we ever add optimizations to do so we should allow an + // implicit check here (as long as the address falls in the first page). + return false; + } + void SetType(Primitive::Type type) { type_ = type; } HInstruction* GetArray() const { return InputAt(0); } @@ -2265,12 +2302,17 @@ class HArraySet : public HTemplateInstruction<3> { SetRawInputAt(2, value); } - bool NeedsEnvironment() const { + bool NeedsEnvironment() const OVERRIDE { // We currently always call a runtime method to catch array store // exceptions. return needs_type_check_; } + bool CanDoImplicitNullCheck() const OVERRIDE { + // TODO: Same as for ArrayGet. + return false; + } + void ClearNeedsTypeCheck() { needs_type_check_ = false; } @@ -2313,11 +2355,12 @@ class HArrayLength : public HExpression<1> { SetRawInputAt(0, array); } - virtual bool CanBeMoved() const { return true; } - virtual bool InstructionDataEquals(HInstruction* other) const { + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { UNUSED(other); return true; } + bool CanDoImplicitNullCheck() const OVERRIDE { return true; } DECLARE_INSTRUCTION(ArrayLength); |