// Check that a location summary is consistent with an instruction. bool CodeGenerator::CheckTypeConsistency(HInstruction* instruction) {
LocationSummary* locations = instruction->GetLocations(); if (locations == nullptr) { returntrue;
}
bool CodeGenerator::ShouldCheckGCCard(DataType::Type type,
HInstruction* value,
WriteBarrierKind write_barrier_kind) const { const CompilerOptions& options = GetCompilerOptions(); constbool result = // Check the GC card in debug mode,
options.EmitRunTimeChecksInDebugMode() && // only for CC GC,
options.EmitReadBarrier() && // and if we eliminated the write barrier in WBE.
!StoreNeedsWriteBarrier(type, value, write_barrier_kind) &&
CodeGenerator::StoreNeedsWriteBarrier(type, value);
~DisassemblyScope() { // We avoid building this data when we know it will not be used. if (codegen_.GetDisassemblyInformation() != nullptr) {
codegen_.GetDisassemblyInformation()->AddInstructionInterval(
instruction_, start_offset_, codegen_.GetAssembler().CodeSize());
}
}
// The register allocator already called `InitializeCodeGeneration`, // where the frame size has been computed.
DCHECK(block_order_ != nullptr);
Initialize();
for (size_t e = block_order_->size(); current_block_index_ < e; ++current_block_index_) {
HBasicBlock* block = (*block_order_)[current_block_index_]; // Don't generate code for an empty block. Its predecessors will branch to its successor // directly. Also, the label of that block will not be emitted, so this helps catch // errors where we reference that label. if (IsSingleJump(block)) { continue;
}
Bind(block); // This ensures that we have correct native line mapping for all native instructions. // It is necessary to make stepping over a statement work. Otherwise, any initial // instructions (e.g. moves) would be assumed to be the start of next statement.
MaybeRecordNativeDebugInfoForBlockEntry(block->GetDexPc()); for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current(); if (current->HasEnvironment()) { // Catch StackMaps are dealt with later on in `RecordCatchBlockInfo`. if (block->IsCatchBlock() && block->GetFirstInstruction() == current) {
DCHECK(current->IsNop()); continue;
}
// Create stackmap for HNop or any instruction which calls native code. // Note that we need correct mapping for the native PC of the call instruction, // so the runtime's stackmap is not sufficient since it is at PC after the call.
MaybeRecordNativeDebugInfo(current, block->GetDexPc());
}
DisassemblyScope disassembly_scope(current, *this);
DCHECK(CheckTypeConsistency(current));
instruction_visitor->Dispatch(current);
}
}
GenerateSlowPaths();
// Emit catch stack maps at the end of the stack map stream as expected by the // runtime exception handler. if (graph_->HasTryCatch()) {
RecordCatchBlockInfo();
}
// Finalize instructions in the assembler.
Finalize();
if (invoke->IsInvokeStaticOrDirect()) {
HInvokeStaticOrDirect* call = invoke->AsInvokeStaticOrDirect();
MethodLoadKind method_load_kind = call->GetMethodLoadKind();
CodePtrLocation code_ptr_location = call->GetCodePtrLocation(); if (code_ptr_location == CodePtrLocation::kCallCriticalNative ||
method_load_kind == MethodLoadKind::kRecursive) { // For `kCallCriticalNative` we need the current method as the hidden argument // if we reach the dlsym lookup stub for @CriticalNative.
locations->SetInAt(call->GetCurrentMethodIndex(), visitor->GetMethodLocation());
} else {
locations->AddTemp(visitor->GetMethodLocation()); if (method_load_kind == MethodLoadKind::kRuntimeCall) {
locations->SetInAt(call->GetCurrentMethodIndex(), Location::RequiresCoreRegister());
}
}
} elseif (!invoke->IsInvokePolymorphic()) {
locations->AddTemp(visitor->GetMethodLocation());
}
}
void CodeGenerator::PrepareCriticalNativeArgumentMoves(
HInvokeStaticOrDirect* invoke, /*inout*/InvokeDexCallingConventionVisitor* visitor, /*out*/HParallelMove* parallel_move) {
LocationSummary* locations = invoke->GetLocations(); for (size_t i = 0, num = invoke->GetNumberOfArguments(); i != num; ++i) {
Location in_location = locations->InAt(i);
DataType::Type type = invoke->InputAt(i)->GetType();
DCHECK_NE(type, DataType::Type::kReference);
Location out_location = visitor->GetNextLocation(type); if (out_location.IsStackSlot() || out_location.IsDoubleStackSlot()) { // Stack arguments will need to be moved after adjusting the SP.
parallel_move->AddMove(in_location, out_location, type, /*instruction=*/ nullptr);
} else { // Register arguments should have been assigned their final locations for register allocation.
DCHECK(out_location.Equals(in_location)) << in_location << " -> " << out_location;
}
}
}
void CodeGenerator::FinishCriticalNativeFrameSetup(size_t out_frame_size, /*inout*/HParallelMove* parallel_move) {
DCHECK_NE(out_frame_size, 0u);
IncreaseFrame(out_frame_size); // Adjust the source stack offsets by `out_frame_size`, i.e. the additional // frame size needed for outgoing stack arguments. for (size_t i = 0, num = parallel_move->NumMoves(); i != num; ++i) {
MoveOperands* operands = parallel_move->MoveOperandsAt(i);
Location source = operands->GetSource(); if (operands->GetSource().IsStackSlot()) {
operands->SetSource(Location::StackSlot(source.GetStackIndex() + out_frame_size));
} elseif (operands->GetSource().IsDoubleStackSlot()) {
operands->SetSource(Location::DoubleStackSlot(source.GetStackIndex() + out_frame_size));
}
} // Emit the moves.
GetMoveResolver()->EmitNativeCode(parallel_move);
}
// The access check is unnecessary but we do not want to introduce // extra entrypoints for the codegens that do not support some // invoke type and fall back to the runtime call.
// Initialize to anything to silent compiler warnings.
QuickEntrypointEnum entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck; switch (invoke->GetInvokeType()) { case kStatic:
entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck; break; case kDirect:
entrypoint = kQuickInvokeDirectTrampolineWithAccessCheck; break; case kSuper:
entrypoint = kQuickInvokeSuperTrampolineWithAccessCheck; break; case kVirtual: case kInterface: case kPolymorphic: case kCustom:
LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
UNREACHABLE();
}
// Initialize to anything to silent compiler warnings.
QuickEntrypointEnum entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck; switch (invoke->GetInvokeType()) { case kStatic:
entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck; break; case kDirect:
entrypoint = kQuickInvokeDirectTrampolineWithAccessCheck; break; case kVirtual:
entrypoint = kQuickInvokeVirtualTrampolineWithAccessCheck; break; case kSuper:
entrypoint = kQuickInvokeSuperTrampolineWithAccessCheck; break; case kInterface:
entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck; break; case kPolymorphic: case kCustom:
LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
UNREACHABLE();
}
InvokeRuntime(entrypoint, invoke);
}
void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke,
SlowPathCode* slow_path) { // invoke-polymorphic does not use a temporary to convey any additional information (e.g. a // method index) since it requires multiple info from the instruction (registers A, B, H). Not // using the reservation has no effect on the registers used in the runtime call.
QuickEntrypointEnum entrypoint = kQuickInvokePolymorphic;
InvokeRuntime(entrypoint, invoke, slow_path);
}
if (is_instance) { // Add the `this` object for instance field accesses.
locations->SetInAt(0, calling_convention.GetObjectLocation());
}
// Note that pSetXXStatic/pGetXXStatic always takes/returns an int or int64 // regardless of the type. Because of that we forced to special case // the access to floating point values. if (is_get) { if (DataType::IsFloatingPointType(field_type)) { // The return value will be stored in regular registers while register // allocator expects it in a floating point register. // Note We don't need to request additional temps because the return // register(s) are already blocked due the call and they may overlap with // the input or field index. // The transfer between the two will be done at codegen level.
locations->SetOut(calling_convention.GetFpuLocation(field_type));
} else {
locations->SetOut(calling_convention.GetReturnLocation(field_type));
}
} else {
size_t set_index = is_instance ? 1 : 0; if (DataType::IsFloatingPointType(field_type)) { // The set value comes from a float location while the calling convention // expects it in a regular register location. Allocate a temp for it and // make the transfer at codegen.
AddLocationAsTemp(calling_convention.GetSetValueLocation(field_type, is_instance), locations);
locations->SetInAt(set_index, calling_convention.GetFpuLocation(field_type));
} else {
locations->SetInAt(set_index,
calling_convention.GetSetValueLocation(field_type, is_instance));
}
}
}
if (!is_get && DataType::IsFloatingPointType(field_type)) { // Copy the float value to be set into the calling convention register. // Note that using directly the temp location is problematic as we don't // support temp register pairs. To avoid boilerplate conversion code, use // the location from the calling convention.
MoveLocation(calling_convention.GetSetValueLocation(field_type, is_instance),
locations->InAt(is_instance ? 1 : 0),
(DataType::Is64BitType(field_type) ? DataType::Type::kInt64
: DataType::Type::kInt32));
}
std::unique_ptr<CodeGenerator> CodeGenerator::Create(HGraph* graph, const CompilerOptions& compiler_options,
OptimizingCompilerStats* stats) {
ArenaAllocator* allocator = graph->GetAllocator(); switch (compiler_options.GetInstructionSet()) { #ifdef ART_ENABLE_CODEGEN_arm case InstructionSet::kArm: case InstructionSet::kThumb2: { return std::unique_ptr<CodeGenerator>( new (allocator) arm::CodeGeneratorARMVIXL(graph, compiler_options, stats));
} #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: { return std::unique_ptr<CodeGenerator>( new (allocator) arm64::CodeGeneratorARM64(graph, compiler_options, stats));
} #endif #ifdef ART_ENABLE_CODEGEN_riscv64 case InstructionSet::kRiscv64: { return std::unique_ptr<CodeGenerator>( new (allocator) riscv64::CodeGeneratorRISCV64(graph, compiler_options, stats));
} #endif #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: { return std::unique_ptr<CodeGenerator>( new (allocator) x86::CodeGeneratorX86(graph, compiler_options, stats));
} #endif #ifdef ART_ENABLE_CODEGEN_x86_64 case InstructionSet::kX86_64: { return std::unique_ptr<CodeGenerator>( new (allocator) x86_64::CodeGeneratorX86_64(graph, compiler_options, stats));
} #endif default:
UNUSED(allocator);
UNUSED(graph);
UNUSED(stats); return nullptr;
}
}
CodeGenerator::CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
size_t number_of_fpu_registers,
size_t number_of_vector_registers,
RegisterSet callee_saves, const CompilerOptions& compiler_options,
OptimizingCompilerStats* stats,
ArrayRef<constbool> unimplemented_intrinsics)
: frame_size_(0),
first_register_slot_in_slow_path_(0),
callee_saves_(callee_saves),
blocked_registers_(RegisterSet::Empty()),
allocated_registers_(RegisterSet::Empty()),
spilled_registers_(RegisterSet::Empty()),
data_types_requiring_register_pair_(0u),
number_of_core_registers_(number_of_core_registers),
number_of_fpu_registers_(number_of_fpu_registers),
number_of_vector_registers_(number_of_vector_registers),
block_order_(nullptr),
disasm_info_(nullptr),
stats_(stats),
graph_(graph),
compiler_options_(compiler_options),
current_slow_path_(nullptr),
current_block_index_(0),
is_leaf_(true), // We need the current method for baseline in case we reach the hotness threshold. // As a side effect this makes the frame non-empty.
requires_current_method_(GetGraph()->IsCompilingBaseline()),
code_generation_data_(),
unimplemented_intrinsics_(unimplemented_intrinsics) {
DCHECK_LE(number_of_core_registers_, BitSizeOf<uint32_t>());
DCHECK_LE(number_of_fpu_registers_, BitSizeOf<uint32_t>());
DCHECK_LE(number_of_vector_registers, BitSizeOf<uint32_t>());
if (GetGraph()->IsCompilingOsr()) { // Make OSR methods have all registers spilled, this simplifies the logic of // jumping to the compiled code directly.
allocated_registers_ = allocated_registers_.Union(callee_saves_);
}
}
staticvoid CheckCovers(uint32_t dex_pc, const HGraph& graph, const CodeInfo& code_info, const ArenaVector<HSuspendCheck*>& loop_headers,
ArenaVector<size_t>* covered) { for (size_t i = 0; i < loop_headers.size(); ++i) { if (loop_headers[i]->GetDexPc() == dex_pc) { if (graph.IsCompilingOsr()) {
DCHECK(code_info.GetOsrStackMapForDexPc(dex_pc).IsValid());
}
++(*covered)[i];
}
}
}
// Debug helper to ensure loop entries in compiled code are matched by // dex branch instructions. staticvoid CheckLoopEntriesCanBeUsedForOsr(const HGraph& graph, const CodeInfo& code_info, const dex::CodeItem& code_item) { if (graph.HasTryCatch()) { // One can write loops through try/catch, which we do not support for OSR anyway. return;
}
ArenaVector<HSuspendCheck*> loop_headers(graph.GetAllocator()->Adapter(kArenaAllocMisc)); for (HBasicBlock* block : graph.GetReversePostOrder()) { if (block->IsLoopHeader()) {
HSuspendCheck* suspend_check = block->GetLoopInformation()->GetSuspendCheck(); if (suspend_check != nullptr && !suspend_check->GetEnvironment()->IsFromInlinedInvoke()) {
loop_headers.push_back(suspend_check);
}
}
}
ArenaVector<size_t> covered(
loop_headers.size(), 0, graph.GetAllocator()->Adapter(kArenaAllocMisc)); for (const DexInstructionPcPair& pair : CodeItemInstructionAccessor(graph.GetDexFile(),
&code_item)) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); if (instruction.IsBranch()) {
uint32_t target = dex_pc + instruction.GetTargetOffset();
CheckCovers(target, graph, code_info, loop_headers, &covered);
} elseif (instruction.IsSwitch()) {
DexSwitchTable table(instruction, dex_pc);
uint16_t num_entries = table.GetNumEntries();
size_t offset = table.GetFirstValueIndex();
// Use a larger loop counter type to avoid overflow issues. for (size_t i = 0; i < num_entries; ++i) { // The target of the case.
uint32_t target = dex_pc + table.GetEntryAt(i + offset);
CheckCovers(target, graph, code_info, loop_headers, &covered);
}
}
}
for (size_t i = 0; i < covered.size(); ++i) {
DCHECK_NE(covered[i], 0u) << "Loop in compiled code has no dex branch equivalent";
}
}
void CodeGenerator::RecordPcInfo(HInstruction* instruction,
SlowPathCode* slow_path, bool native_debug_info) { // Only for native debuggable apps we take a look at the dex_pc from the instruction itself. For // the regular case, we retrieve the dex_pc from the instruction's environment.
DCHECK_IMPLIES(native_debug_info, GetCompilerOptions().GetNativeDebuggable());
DCHECK_IMPLIES(!native_debug_info, instruction->HasEnvironment()) << *instruction;
RecordPcInfo(instruction,
native_debug_info ? instruction->GetDexPc() : kNoDexPc,
GetAssembler()->CodePosition(),
slow_path,
native_debug_info);
}
void CodeGenerator::RecordPcInfo(HInstruction* instruction,
uint32_t dex_pc,
uint32_t native_pc,
SlowPathCode* slow_path, bool native_debug_info) {
DCHECK(instruction != nullptr); // Only for native debuggable apps we take a look at the dex_pc from the instruction itself. For // the regular case, we retrieve the dex_pc from the instruction's environment.
DCHECK_IMPLIES(native_debug_info, GetCompilerOptions().GetNativeDebuggable());
DCHECK_IMPLIES(!native_debug_info, instruction->HasEnvironment()) << *instruction;
LocationSummary* locations = instruction->GetLocations();
uint32_t register_mask = 0u;
BitVector* stack_mask = nullptr; if (locations->CanCall()) {
stack_mask = locations->GetStackMask();
register_mask = locations->GetRegisterMask();
DCHECK_EQ(register_mask & ~locations->GetLiveRegisters()->GetCoreRegisterSet(), 0u); if (locations->OnlyCallsOnSlowPath()) { // In case of slow path, we currently set the location of caller-save registers // to register (instead of their stack location when pushed before the slow-path // call). Therefore register_mask contains both callee-save and caller-save // registers that hold objects. We must remove the spilled caller-save from the // mask, since they will be overwritten by the callee.
uint32_t spills = GetSlowPathSpills(locations).GetCoreRegisterSet();
register_mask &= ~spills;
} else { // The register mask must be a subset of callee-save registers.
DCHECK_EQ(register_mask & GetCalleeSaveRegisters().GetCoreRegisterSet(), register_mask);
}
}
void CodeGenerator::MaybeRecordNativeDebugInfoForBlockEntry(uint32_t dex_pc) { if (GetCompilerOptions().GetNativeDebuggable() && dex_pc != kNoDexPc) { if (HasStackMapAtCurrentPc()) { // Ensure that we do not collide with the stack map of the previous instruction.
GenerateNop();
}
RecordPcInfoForFrameOrBlockEntry(dex_pc);
}
}
void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) { if (GetCompilerOptions().GetNativeDebuggable() && dex_pc != kNoDexPc) { if (HasStackMapAtCurrentPc()) { // Ensure that we do not collide with the stack map of the previous instruction.
GenerateNop();
}
RecordPcInfo(instruction, slow_path, /* native_debug_info= */ true);
}
}
for (HBasicBlock* block : *block_order_) { if (!block->IsCatchBlock()) { continue;
}
// Get the outer dex_pc. We save the full environment list for DCHECK purposes in kIsDebugBuild.
std::vector<uint32_t> dex_pc_list_for_verification; if (kIsDebugBuild) {
dex_pc_list_for_verification.push_back(block->GetDexPc());
}
DCHECK(block->GetFirstInstruction()->IsNop());
DCHECK(block->GetFirstInstruction()->AsNop()->NeedsEnvironment());
HEnvironment* const environment = block->GetFirstInstruction()->GetEnvironment();
DCHECK(environment != nullptr);
HEnvironment* outer_environment = environment; while (outer_environment->GetParent() != nullptr) {
outer_environment = outer_environment->GetParent(); if (kIsDebugBuild) {
dex_pc_list_for_verification.push_back(outer_environment->GetDexPc());
}
}
if (kIsDebugBuild) { // dex_pc_list_for_verification is set from innnermost to outermost. Let's reverse it // since we are expected to pass from outermost to innermost.
std::reverse(dex_pc_list_for_verification.begin(), dex_pc_list_for_verification.end());
DCHECK_EQ(dex_pc_list_for_verification.front(), outer_environment->GetDexPc());
}
void CodeGenerator::EmitVRegInfo(HEnvironment* environment,
SlowPathCode* slow_path, bool is_for_catch_handler) {
StackMapStream* stack_map_stream = GetStackMapStream(); // Walk over the environment, and record the location of dex registers. for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
HInstruction* current = environment->GetInstructionAt(i); if (current == nullptr) {
stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0); continue;
}
if (emit_inline_info) { // We emit the parent environment first.
EmitEnvironment(environment->GetParent(),
slow_path,
needs_vreg_info,
is_for_catch_handler, /* innermost_environment= */ false);
stack_map_stream->BeginInlineInfoEntry(environment->GetMethod(),
environment->GetDexPc(),
needs_vreg_info ? environment->Size() : 0,
&graph_->GetDexFile(), this);
}
// If a dex register map is not required we just won't emit it. if (needs_vreg_info) { if (innermost_environment && is_for_catch_handler) {
EmitVRegInfoOnlyCatchPhis(environment);
} else {
EmitVRegInfo(environment, slow_path, is_for_catch_handler);
}
}
if (emit_inline_info) {
stack_map_stream->EndInlineInfoEntry();
}
}
LocationSummary* CodeGenerator::CreateThrowingSlowPathLocations(HInstruction* instruction,
RegisterSet caller_saves) { // Note: Using kNoCall allows the method to be treated as leaf (and eliminate the // HSuspendCheck from entry block). However, it will still get a valid stack frame // because the HNullCheck needs an environment.
LocationSummary::CallKind call_kind = LocationSummary::kNoCall; // When throwing from a try block, we may need to retrieve dalvik registers from // physical registers and we also need to set up stack mask for GC. This is // implicitly achieved by passing kCallOnSlowPath to the LocationSummary. bool can_throw_into_catch_block = instruction->CanThrowIntoCatchBlock(); if (can_throw_into_catch_block) {
call_kind = LocationSummary::kCallOnSlowPath;
}
LocationSummary* locations =
LocationSummary::Create(GetGraph()->GetAllocator(), instruction, call_kind); if (can_throw_into_catch_block && compiler_options_.GetImplicitNullChecks()) {
locations->SetCustomSlowPathCallerSaves(caller_saves); // Default: no caller-save registers.
}
DCHECK(!instruction->HasUses()); return locations;
}
for (size_t i = 0, num_moves = spills->NumMoves(); i != num_moves; ++i) {
Location dest = spills->MoveOperandsAt(i)->GetDestination(); // All parallel moves in loop headers are spills.
DCHECK(dest.IsStackSlot() || dest.IsDoubleStackSlot() || dest.IsSIMDStackSlot()) << dest; // Clear the stack bit marking a reference. Do not bother to check if the spill is // actually a reference spill, clearing bits that are already zero is harmless.
locations->ClearStackBit(dest.GetStackIndex() / kVRegSize);
}
}
bool CodeGenerator::StoreNeedsWriteBarrier(DataType::Type type,
HInstruction* value,
WriteBarrierKind write_barrier_kind) const { // Check that null value is not represented as an integer constant.
DCHECK_IMPLIES(type == DataType::Type::kReference, !value->IsIntConstant()); // Branch profiling currently doesn't support running optimizations. return (GetGraph()->IsCompilingBaseline() && compiler_options_.ProfileBranches())
? CodeGenerator::StoreNeedsWriteBarrier(type, value)
: write_barrier_kind != WriteBarrierKind::kDontEmit;
}
void CodeGenerator::ValidateInvokeRuntime(QuickEntrypointEnum entrypoint,
HInstruction* instruction,
SlowPathCode* slow_path) { // Ensure that the call kind indication given to the register allocator is // coherent with the runtime call generated. if (slow_path == nullptr) {
DCHECK(instruction->GetLocations()->WillCall())
<< "instruction->DebugName()=" << instruction->DebugName();
} else {
DCHECK(instruction->GetLocations()->CallsOnSlowPath() || slow_path->IsFatal())
<< "instruction->DebugName()=" << instruction->DebugName()
<< " slow_path->GetDescription()=" << slow_path->GetDescription();
}
// Check that the GC side effect is set when required. // TODO: Reverse EntrypointCanTriggerGC if (EntrypointCanTriggerGC(entrypoint)) { if (slow_path == nullptr) {
DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()))
<< "instruction->DebugName()=" << instruction->DebugName()
<< " instruction->GetSideEffects().ToString()="
<< instruction->GetSideEffects().ToString();
} else { // 'CanTriggerGC' side effect is used to restrict optimization of instructions which depend // on GC (e.g. IntermediateAddress) - to ensure they are not alive across GC points. However // if execution never returns to the compiled code from a GC point this restriction is // unnecessary - in particular for fatal slow paths which might trigger GC.
DCHECK((slow_path->IsFatal() && !instruction->GetLocations()->WillCall()) ||
instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) || // When (non-Baker) read barriers are enabled, some instructions // use a slow path to emit a read barrier, which does not trigger // GC.
(EmitNonBakerReadBarrier() &&
(instruction->IsInstanceFieldGet() ||
instruction->IsStaticFieldGet() ||
instruction->IsArrayGet() ||
instruction->IsLoadClass() ||
instruction->IsLoadString() ||
instruction->IsInstanceOf() ||
instruction->IsCheckCast() ||
(instruction->IsInvokeVirtual() && instruction->GetLocations()->Intrinsified()))))
<< "instruction->DebugName()=" << instruction->DebugName()
<< " instruction->GetSideEffects().ToString()="
<< instruction->GetSideEffects().ToString()
<< " slow_path->GetDescription()=" << slow_path->GetDescription() << std::endl
<< "Instruction and args: " << instruction->DumpWithArgs();
}
} else { // The GC side effect is not required for the instruction. But the instruction might still have // it, for example if it calls other entrypoints requiring it.
}
for (uint32_t i : LowToHighBits(spills.GetFpuRegisterSet())) {
DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, i);
}
}
LocationSummary* CodeGenerator::CreateSystemArrayCopyLocationSummary(
HInvoke* invoke, int32_t length_threshold, size_t num_temps) { // Check to see if we have known failures that will cause us to have to bail out // to the runtime, and just generate the runtime call directly.
HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstantOrNull();
HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstantOrNull();
// The positions must be non-negative. if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
(dest_pos != nullptr && dest_pos->GetValue() < 0)) { // We will have to fail anyways. return nullptr;
}
// The length must be >= 0. If a positive `length_threshold` is provided, lengths // greater or equal to the threshold are also handled by the normal implementation.
HIntConstant* length = invoke->InputAt(4)->AsIntConstantOrNull(); if (length != nullptr) {
int32_t len = length->GetValue(); if (len < 0 || (length_threshold > 0 && len >= length_threshold)) { // Just call as normal. return nullptr;
}
}
if (optimizations.GetDestinationIsSource()) { if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) { // We only support backward copying if source and destination are the same. return nullptr;
}
}
if (invoke->GetIntrinsic() == Intrinsics::kSystemArrayCopy &&
(optimizations.GetDestinationIsPrimitiveArray() ||
optimizations.GetSourceIsPrimitiveArray())) { // The generic version: we currently don't intrinsify primitive copying. return nullptr;
}
ScaleFactor CodeGenerator::ScaleFactorForType(DataType::Type type) { switch (type) { case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: return TIMES_1; case DataType::Type::kUint16: case DataType::Type::kInt16: return TIMES_2; case DataType::Type::kInt32: case DataType::Type::kUint32: case DataType::Type::kFloat32: case DataType::Type::kReference: return TIMES_4; case DataType::Type::kInt64: case DataType::Type::kUint64: case DataType::Type::kFloat64: return TIMES_8; case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.