usingnamespace vixl::aarch64; // NOLINT(build/namespaces) using vixl::ExactAssemblyScope; using vixl::CodeBufferCheckScope; using vixl::EmissionCheckScope;
using helpers::CPURegisterFrom; using helpers::HeapOperand; using helpers::LocationFrom; using helpers::StackOperandFrom; using helpers::RegisterFrom; using helpers::DRegisterFrom; using helpers::SRegisterFrom; using helpers::WRegisterFrom; using helpers::XRegisterFrom;
// The maximum (meaningful) distance (31) that can be used in an integer shift/rotate operation. static constexpr int32_t kMaxIntShiftDistance = 0x1f;
// The core register or FPU register number we are going to use when a dex // register is stored in stack, and the operation that stores to it needs a // temporary register. static constexpr size_t kResultRegisterForSpill = 0u;
private: // Go over each instruction of the method, and generate code for them. bool ProcessInstructions(); bool ProcessBlock(uint32_t dex_pc);
// Initialize the locations of parameters for this method. bool InitializeParameters();
// Generate code for the frame entry, as well as the suspend check. // Only called when needed. If the frame entry has already been generated, do nothing. bool EnsureHasFrame();
// Generate code for a frame exit. void PopFrameAndReturn();
// Record a stack map at the given dex_pc. void RecordPcInfo(uint32_t dex_pc);
// Generate code to move from one location to another. Note that `hint_type` // is only used for the size of the type (64 or 32 bits), as some operations // come untyped and we default to integer for such operations. The // destination and the source will know the type. bool MoveLocation(Location destination, Location source, DataType::Type hint_type);
// Get a register location for the dex register `reg`. Saves the location into // `vreg_locations_` for next uses of `reg`. // `next` should be the next dex instruction, to help choose the register.
Location CreateNewRegisterLocation(uint32_t reg, DataType::Type type, const Instruction* next);
Location CreateNewLocation(uint32_t reg, DataType::Type type);
// Return the existing register location for `reg`.
Location GetExistingRegisterLocation(uint32_t reg, DataType::Type type);
// Move dex registers holding constants and fpu registers into core registers. Used when // branching. void MoveConstantsAndFpusToRegisters();
// Reset all locations to core registers. Used for branch targets. void ResetLocations();
// Handle the beginning of a branch target. void StartBranchTarget(bool flow_continues, uint32_t dex_pc);
// Whether the branch target is initialized, which means we've already seen a // branch to it. bool BranchTargetIsInitialized(uint32_t dex_pc);
// Update the masks associated to the given dex_pc. Used when dex_pc is a // branch target. void UpdateMasks(uint32_t dex_pc);
// Generate code for one instruction. bool ProcessDexInstruction(const Instruction& instruction,
uint32_t dex_pc, const Instruction* next);
// Setup the arguments for an invoke. bool SetupArguments(InvokeType invoke_type, const InstructionOperands& operands, constchar* shorty, /* out */ uint32_t* obj_reg);
// Generate code for doing a Java invoke. bool HandleInvoke(const Instruction& instruction, uint32_t dex_pc, InvokeType invoke_type);
void AddToWorkQueue(uint32_t dex_pc) { if (!processed_.IsBitSet(dex_pc)) {
work_queue_.push(dex_pc);
}
}
// Update registers and masks for the merge point. void PrepareToBranch(uint32_t dex_pc) { // We are going to branch, move all constants to registers to make the merge // point use the same locations.
MoveConstantsAndFpusToRegisters();
UpdateMasks(dex_pc);
AddToWorkQueue(dex_pc);
}
// Mark whether dex register `vreg_index` is an object. void UpdateRegisterMask(uint32_t vreg_index, bool is_object, bool is_wide) {
DCHECK(!(is_object && is_wide)); // A `vreg_index` greater than kMaximumRegisters means the dex register is // stored in stack. if (vreg_index < kMaximumRegisters) { // Note that the register mask is only useful when there is a frame, so we // use the callee save registers for the mask. if (is_object) {
object_register_mask_ |= (1 << kAvailableCalleeSaveRegisters[vreg_index].GetCode());
} else {
object_register_mask_ &= ~(1 << kAvailableCalleeSaveRegisters[vreg_index].GetCode());
}
} else { if (is_object) {
object_stack_mask_.SetBit(GetStackSlot(vreg_index) / kVRegSize);
} else {
object_stack_mask_.ClearBit(GetStackSlot(vreg_index) / kVRegSize); if (is_wide) {
object_stack_mask_.ClearBit(GetStackSlot(vreg_index + 1) / kVRegSize);
}
}
}
}
// Get the label associated with the given `dex_pc`.
vixl::aarch64::Label* GetLabelOf(uint32_t dex_pc) { return &branch_targets_[dex_pc];
}
// If we need to abort compilation, clear branch targets, required by vixl. void AbortCompilation() { for (vixl::aarch64::Label& label : branch_targets_) { if (label.IsLinked()) {
__ Bind(&label);
}
}
}
// A bit vector marking which instructions have already been processed.
BitVectorView<size_t> processed_;
// Work queue for the compiler, containing dex_pc to start the compilation.
ArenaPriorityQueue<uint32_t, std::greater<uint32_t>> work_queue_;
// The current location of each dex register.
ArenaVector<Location> vreg_locations_;
// A vector of size code units for dex pcs that are branch targets.
ArenaVector<vixl::aarch64::Label> branch_targets_;
// For dex pcs that are branch targets, the register mask and the stack mask that // will be used at the point of that pc.
ArenaVector<uint64_t> object_register_masks_;
ArenaVector<ArenaBitVector*> object_stack_masks_;
// For dex pcs that are branch targets, the mask for non-null objects that will // be used at the point of that pc.
ArenaVector<uint64_t> is_non_null_masks_;
// Dex pcs that are catch targets.
BitVectorView<size_t> catch_pcs_;
// If we are compiling this method with loop support, the dex pcs that are branch targets. // Empty otherwise.
BitVectorView<size_t> branch_pcs_;
// Pair of {dex_pc, native_pc} collected during compilation, used when // generating stack map entries for catch instructions at the end of // compilation.
ArenaVector<std::pair<uint32_t, uint32_t>> catch_stack_maps_;
// Whether we've created a frame for this compiled method. bool has_frame_;
// Whether we need dex register info in stack maps. bool needs_vreg_info_;
// CPU registers that have been spilled in the frame.
uint32_t core_spill_mask_;
// FPU registers that have been spilled in the frame.
uint32_t fpu_spill_mask_;
// The current mask to know which core register holds an object.
uint64_t object_register_mask_;
// The current mask to know which stack entry holds an object.
ArenaBitVector object_stack_mask_;
// The current mask to know if a dex register is known non-null.
uint64_t is_non_null_mask_;
// At the end of an instruction, the register we need to spill to stack. -1 if // there is no register to spill.
std::pair<uint32_t, DataType::Type> register_to_spill_;
// Temporary registers for when we need to have dex registers in stack.
uint32_t temp_core_register_;
uint32_t temp_fpu_register_;
// The return type of the compiled method. Saved to avoid re-computing it on // the return instruction.
DataType::Type return_type_;
// The return type of the last invoke instruction.
DataType::Type previous_invoke_return_type_;
// If non-empty, the reason the compilation could not be finished. constchar* unimplemented_reason_ = nullptr;
// Set to true when we hit a back edge and we haven't collected loop headers. bool recompile_with_loop_support_ = false;
};
if (number_of_vregs > kMaximumRegisters) { // Generate the frame, but don't emit the suspend check yet, as we haven't // updated the register and stack masks yet. if (!GenerateFrame()) { returnfalse;
}
needs_spill = true;
}
InvokeDexCallingConventionVisitorARM64 convention; if (!dex_compilation_unit_.IsStatic()) { // Add the implicit 'this' argument, not expressed in the signature.
vreg_locations_[vreg_parameter_index] = convention.GetNextLocation(DataType::Type::kReference); if (needs_spill) {
Location new_location = CreateNewLocation(vreg_parameter_index, DataType::Type::kReference);
DCHECK(vreg_locations_[vreg_parameter_index].IsCoreRegister());
MoveLocation(new_location, vreg_locations_[vreg_parameter_index], DataType::Type::kReference);
vreg_locations_[vreg_parameter_index] = new_location;
}
UpdateLocal(vreg_parameter_index, /* is_object= */ true, /* is_wide= */ false, /* can_be_null= */ false);
++vreg_parameter_index;
--number_of_parameters;
}
for (int i = 0, shorty_pos = 1;
i < number_of_parameters;
i++, shorty_pos++, vreg_parameter_index++) {
DataType::Type type = DataType::FromShorty(shorty[shorty_pos]);
Location location = convention.GetNextLocation(type); if (location.IsDoubleStackSlot() || location.IsStackSlot()) { // We only handle single stack slots in the fast compiler. The // dex instructions know if they need two stack slot entries or one.
location = Location::StackSlot(location.GetStackIndex() + GetFrameSize());
} if (needs_spill) {
Location new_location = CreateNewLocation(vreg_parameter_index, type);
MoveLocation(new_location, location, type);
vreg_locations_[vreg_parameter_index] = new_location;
} else {
vreg_locations_[vreg_parameter_index] = location;
} bool is_wide = DataType::Is64BitType(type);
UpdateLocal(vreg_parameter_index, /* is_object= */ (type == DataType::Type::kReference),
is_wide, /* can_be_null= */ true); if (is_wide) {
++i;
++vreg_parameter_index;
}
}
return_type_ = DataType::FromShorty(shorty[0]);
if (needs_spill) { // We can now generate the suspend check.
GenerateSuspendCheck(/* dex_pc= */ 0u);
}
if (branch_pcs_.IsAnyBitSet()) { // Generate the frame now. We don't want to create it lazily as the branch // instruction going backwards might branch to a dex pc lower than where the // frame was created. if (!EnsureHasFrame()) { returnfalse;
}
}
void FastCompilerARM64::StartBranchTarget(bool flow_continues, uint32_t dex_pc) { if (flow_continues) { // Emulate a branch to this pc.
PrepareToBranch(dex_pc);
} else {
DCHECK(BranchTargetIsInitialized(dex_pc)) << "0x" << std::hex << dex_pc; // Otherwise reset locations to known locations.
ResetLocations();
}
// Set new masks based on all incoming edges. if (IsBranchTarget(dex_pc)) { // Disable non-null optimizations at potential loop headers. This can avoid // the need to re-iterate for finding a fixed point for non-null masks.
is_non_null_mask_ = 0u;
} else {
is_non_null_mask_ = is_non_null_masks_[dex_pc];
}
object_register_mask_ = object_register_masks_[dex_pc];
DCHECK_NE(object_stack_masks_[dex_pc], nullptr);
object_stack_mask_.Copy(object_stack_masks_[dex_pc]);
}
bool FastCompilerARM64::ProcessBlock(uint32_t dex_pc) { // In the case of the first instruction, we consider the flow to continue from // prologue to dex pc 0 to get the DEX registers into the right locations. bool flow_continues = (dex_pc == 0u);
DexInstructionIterator it = GetCodeItemAccessor().InstructionsFrom(dex_pc).begin();
DexInstructionIterator end = GetCodeItemAccessor().end(); do {
DexInstructionPcPair pair = *it; if (processed_.IsBitSet(pair.DexPc())) { if (flow_continues) { // If the previous instruction was flowing into its following // instruction, we need to branch where this following instruction // has been emitted.
PrepareToBranch(pair.DexPc());
__ B(GetLabelOf(pair.DexPc()));
} break;
}
processed_.SetBit(pair.DexPc());
++it;
// Fetch the next instruction as a micro-optimization currently only used // for optimizing returns. const Instruction* next = nullptr; if (it != end) { const DexInstructionPcPair& next_pair = *it; if (GetLabelOf(next_pair.DexPc())->IsLinked() ||
IsBranchTarget(next_pair.DexPc()) ||
catch_pcs_.IsBitSet(next_pair.DexPc())) { // Disable the micro-optimization, as the next instruction is a branch // target.
next = nullptr;
} else {
next = &next_pair.Inst();
}
}
vixl::aarch64::Label* label = GetLabelOf(pair.DexPc()); bool is_catch = catch_pcs_.IsBitSet(pair.DexPc()); bool is_branch_target = IsBranchTarget(pair.DexPc()); bool is_linked = label->IsLinked(); if (is_catch) { if (!BranchTargetIsInitialized(pair.DexPc())) { // The catch handler is in the normal flow (for example at dex pc 0), but // we need to know the masks at throwing instruction. Recompile with loop // support.
unimplemented_reason_ = "Loop retry";
recompile_with_loop_support_ = true; returnfalse;
}
catch_stack_maps_.push_back(std::make_pair(pair.DexPc(), GetAssembler()->CodePosition()));
} if (is_linked || is_branch_target || is_catch) {
StartBranchTarget(flow_continues, pair.DexPc());
} if (is_linked || is_branch_target) {
__ Bind(label);
}
// If the instruction can throw, emulate a branch to each catch handler by // updating dex register masks. if (ThrowsIntoCatchHandler(pair.Inst())) { const dex::TryItem* try_item = GetCodeItemAccessor().FindTryItem(pair.DexPc()); if (try_item != nullptr) { for (CatchHandlerIterator iterator(GetCodeItemAccessor(), *try_item);
iterator.HasNext();
iterator.Next()) { if (iterator.GetHandlerAddress() <= pair.DexPc() &&
!CanHandleBackwardsBranch(iterator.GetHandlerAddress())) { returnfalse;
}
UpdateMasks(iterator.GetHandlerAddress());
AddToWorkQueue(iterator.GetHandlerAddress());
}
}
}
if (!ProcessDexInstruction(pair.Inst(), pair.DexPc(), next)) {
DCHECK(HitUnimplemented()); returnfalse;
}
ResetTempRegisters(); if (NeedsToSpill()) {
Location stack_location =
CreateNewLocation(register_to_spill_.first, register_to_spill_.second);
Location reg_location = DataType::IsFloatingPointType(register_to_spill_.second)
? Location::FpuRegister(kResultRegisterForSpill)
: Location::CoreRegister(kResultRegisterForSpill);
MoveLocation(stack_location, reg_location, register_to_spill_.second);
vreg_locations_[register_to_spill_.first] = stack_location; if (DataType::Is64BitType(register_to_spill_.second)) {
DCHECK(vreg_locations_[register_to_spill_.first + 1].IsInvalid());
}
ResetRegisterToSpill();
} // Note: There may be no Thread for gtests.
DCHECK(Thread::Current() == nullptr || !Thread::Current()->IsExceptionPending())
<< GetDexFile().PrettyMethod(dex_compilation_unit_.GetDexMethodIndex())
<< " " << pair.Inst().Name() << "@" << pair.DexPc();
// For the next instruction, let it know if the previous instruction was // flowing through.
flow_continues = pair.Inst().CanFlowThrough(); if (!flow_continues) { break;
}
} while (it != end); returntrue;
}
Location FastCompilerARM64::CreateNewRegisterLocation(uint32_t reg,
DataType::Type type, const Instruction* next) { if (DataType::Is64BitType(type)) { // To prevent branch points from moving dex registers which are used for // the other half of 64bit types, we invalidate those registers.
vreg_locations_[reg + 1] = Location();
}
if (next != nullptr &&
(next->Opcode() == Instruction::RETURN_OBJECT || next->Opcode() == Instruction::RETURN) &&
(next->VRegA_11x() == reg)) { // If the next instruction is a return, use the return register from the calling // convention.
InvokeDexCallingConventionVisitorARM64 convention;
vreg_locations_[reg] = convention.GetReturnLocation(return_type_); return vreg_locations_[reg];
}
void FastCompilerARM64::RecordPcInfo(uint32_t dex_pc) {
DCHECK(has_frame_);
uint32_t native_pc = GetAssembler()->CodePosition();
StackMapStream* stack_map_stream = code_generation_data_->GetStackMapStream();
CHECK_EQ(object_register_mask_ & callee_saved_core_registers.GetList(), object_register_mask_);
ArenaBitVector* stack_mask = nullptr; if (object_stack_mask_.IsAnyBitSet()) {
stack_mask = new (allocator_) ArenaBitVector(allocator_, /*start_bits=*/ 0, /*expandable=*/ true);
stack_mask->Copy(&object_stack_mask_);
}
stack_map_stream->BeginStackMapEntry(dex_pc,
native_pc,
object_register_mask_,
stack_mask,
StackMap::Kind::Default,
needs_vreg_info_); if (needs_vreg_info_) { using Kind = DexRegisterLocation::Kind;
uint32_t size = vreg_locations_.size(); for (uint32_t i = 0; i < size; ++i) {
Location location = vreg_locations_[i]; switch (location.GetKind()) { case Location::kConstant: { if (location.GetConstant()->IsLongConstant()) {
int64_t value = location.GetConstant()->AsLongConstant()->GetValue();
stack_map_stream->AddDexRegisterEntry(Kind::kConstant, Low32Bits(value));
stack_map_stream->AddDexRegisterEntry(Kind::kConstant, High32Bits(value));
++i;
DCHECK_LT(i, size);
} else {
DCHECK(location.GetConstant()->IsIntConstant());
int32_t value = location.GetConstant()->AsIntConstant()->GetValue();
stack_map_stream->AddDexRegisterEntry(Kind::kConstant, value);
} break;
}
case Location::kStackSlot: {
stack_map_stream->AddDexRegisterEntry(Kind::kInStack, location.GetStackIndex()); // Note: if we were using the fast compiler for debuggable, we would // need to emit another `kInStack` here for long/double values. This would // require knowing if the current entry is a long/double.
DCHECK(!compiler_options_.GetDebuggable()); break;
}
case Location::kCoreRegister: {
stack_map_stream->AddDexRegisterEntry(Kind::kInRegister, location.reg()); // Note: if we were using the fast compiler for debuggable, we would // need to emit a `kInRegisterHi` here for long values. This would // require knowing if the current entry is a long.
DCHECK(!compiler_options_.GetDebuggable()); break;
}
case Location::kFpuRegister: {
stack_map_stream->AddDexRegisterEntry(Kind::kInFpuRegister, location.reg()); // Note: same as above and `kInFpuRegisterHi` for double values.
DCHECK(!compiler_options_.GetDebuggable()); break;
}
case Location::kInvalid: {
stack_map_stream->AddDexRegisterEntry(Kind::kNone, 0); break;
}
bool FastCompilerARM64::EnsureHasFrame() { if (has_frame_) { // Frame entry has already been generated. returntrue;
} if (!GenerateFrame()) { returnfalse;
}
__ Ldrh(counter, MemOperand(method, ArtMethod::HotnessCountOffset().Int32Value()));
__ Cbnz(counter, &increment);
__ Ldr(lr, MemOperand(tr, entrypoint_offset)); // Note: we don't record the call here (and therefore don't generate a stack // map), as the entrypoint should never be suspended.
__ Blr(lr);
__ Bind(&increment);
__ Add(counter, counter, -1);
__ Strh(counter, MemOperand(method, ArtMethod::HotnessCountOffset().Int32Value()));
__ Bind(&done);
}
bool FastCompilerARM64::GenerateFrame() {
DCHECK(!has_frame_);
has_frame_ = true;
uint16_t number_of_vregs = GetCodeItemAccessor().RegistersSize(); for (int i = 0; i < std::min(number_of_vregs, static_cast<uint16_t>(kMaximumRegisters)); ++i) { // Assume any vreg will be held in a callee-save register.
core_spill_mask_ |= (1 << kAvailableCalleeSaveRegisters[i].GetCode()); // TODO: do this lazily for floats, and recompile?
fpu_spill_mask_ |= (1 << kAvailableCalleeSaveFpuRegisters[i].GetCode());
}
core_spill_mask_ |= (1 << lr.GetCode());
size_t frame_size = GetFrameSize(); if (frame_size > GetStackOverflowReservedBytes(InstructionSet::kArm64)) { // This isn't an unimplemented reason, just a hard limit we have in the // runtime about compile code frames.
unimplemented_reason_ = "FrameTooLarge"; returnfalse;
}
code_generation_data_->GetStackMapStream()->BeginMethod(frame_size,
core_spill_mask_,
fpu_spill_mask_,
GetCodeItemAccessor().RegistersSize(), /* is_compiling_baseline= */ false, /* is_debuggable= */ false, /* has_should_deoptimize_flag= */ false, /* is_fast= */ true);
MacroAssembler* masm = GetVIXLAssembler();
{
UseScratchRegisterScope temps(masm); Register temp = temps.AcquireX();
__ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(InstructionSet::kArm64))); // Ensure that between load and RecordPcInfo there are no pools emitted.
ExactAssemblyScope eas(GetVIXLAssembler(),
kInstructionSize,
CodeBufferCheckScope::kExactSize);
__ ldr(wzr, MemOperand(temp, 0));
RecordPcInfo(0);
}
if (number_of_vregs <= kMaximumRegisters) { // Move registers which are currently allocated from caller-saves to callee-saves, // and adjust the offsets of stack locations. for (uint32_t i = 0; i < number_of_vregs; ++i) { if (vreg_locations_[i].IsCoreRegister()) {
Location new_location =
Location::CoreRegister(kAvailableCalleeSaveRegisters[i].GetCode()); if (!MoveLocation(new_location, vreg_locations_[i], DataType::Type::kInt64)) { returnfalse;
}
vreg_locations_[i] = new_location;
} elseif (vreg_locations_[i].IsFpuRegister()) {
Location new_location =
Location::FpuRegister(kAvailableCalleeSaveFpuRegisters[i].GetCode()); if (!MoveLocation(new_location, vreg_locations_[i], DataType::Type::kFloat64)) { returnfalse;
}
vreg_locations_[i] = new_location; if (IsObject(i)) { // The floating point value comes from a zero constant, which we // treat as object. Therefore, ensure the core register associated to // this dex register contains zero.
__ Mov(kAvailableCalleeSaveRegisters[i], vixl::aarch64::xzr);
}
} elseif (vreg_locations_[i].IsStackSlot()) {
DCHECK(IsParameter(i));
vreg_locations_[i] =
Location::StackSlot(vreg_locations_[i].GetStackIndex() + GetFrameSize());
Location new_location =
Location::CoreRegister(kAvailableCalleeSaveRegisters[i].GetCode()); if (!MoveLocation(new_location, vreg_locations_[i], DataType::Type::kInt32)) { returnfalse;
}
vreg_locations_[i] = new_location;
} elseif (vreg_locations_[i].IsConstant() || vreg_locations_[i].IsInvalid()) { // Nothing to do.
} else {
unimplemented_reason_ = "UnhandledLocation"; returnfalse;
}
}
}
// Increment hotness. We use the ArtMethod's counter as we're not allocating a // `ProfilingInfo` object in the fast baseline compiler.
IncrementHotness(kArtMethodRegister); returntrue;
}
uint32_t shorty_index = 1; // Skip the return type. // Handle all parameters except 'this'. for (size_t i = start_index; i < number_of_operands; ++i, ++shorty_index) { // Make sure we don't go over the expected arguments or over the number of // dex registers given. If the instruction was seen as dead by the verifier, // it hasn't been properly checked. char c = shorty[shorty_index]; if (UNLIKELY(c == 0)) {
unimplemented_reason_ = "BogusSignature"; returnfalse;
}
DataType::Type type = DataType::FromShorty(c); bool is_wide = (type == DataType::Type::kInt64) || (type == DataType::Type::kFloat64); if (is_wide && ((i + 1 == number_of_operands) ||
(operands.GetOperand(i) + 1 != operands.GetOperand(i + 1)))) {
unimplemented_reason_ = "BogusSignature"; returnfalse;
}
Location loc = convention.GetNextLocation(type); if (loc.IsDoubleStackSlot()) { // We only handle single stack slots in the fast compiler. The // instructions will be the ones knowing if we need two stack slot entries // or one.
loc = Location::StackSlot(loc.GetStackIndex());
} if (!MoveLocation(loc,
vreg_locations_[operands.GetOperand(i)],
type)) { returnfalse;
} if (is_wide) {
++i;
}
} returntrue;
}
if (resolved_method->IsConstructor() && resolved_method->GetDeclaringClass()->IsObjectClass()) { // Object.<init> is always empty. Return early to not generate a frame. if (kIsDebugBuild) {
CHECK(resolved_method->GetDeclaringClass()->IsVerified());
CodeItemDataAccessor accessor(*resolved_method->GetDexFile(),
resolved_method->GetCodeItem());
CHECK_EQ(accessor.InsnsSizeInCodeUnits(), 1u);
CHECK_EQ(accessor.begin().Inst().Opcode(), Instruction::RETURN_VOID);
} // No need to update `previous_invoke_return_type_`, we know it is not going the // be used. returntrue;
}
if (invoke_type == kSuper) {
resolved_method = method_->SkipAccessChecks()
? FindSuperMethodToCall</*access_check=*/false>(method_index,
resolved_method,
method_,
self)
: FindSuperMethodToCall</*access_check=*/true>(method_index,
resolved_method,
method_,
self); if (resolved_method == nullptr) {
DCHECK(self->IsExceptionPending()) << method_->PrettyMethod();
self->ClearException();
unimplemented_reason_ = "UnresolvedInvokeSuper"; returnfalse;
}
} elseif (invoke_type == kVirtual) {
offset = resolved_method->GetVtableIndex();
} elseif (invoke_type == kInterface) { if (resolved_method->GetDeclaringClass()->IsObjectClass()) { // If the resolved method is from j.l.Object, emit a virtual call instead. // The IMT conflict stub only handles interface methods.
offset = resolved_method->GetVtableIndex();
invoke_type = kVirtual;
} else {
offset = resolved_method->GetImtIndex();
}
}
if (resolved_method->IsStringConstructor()) {
unimplemented_reason_ = "StringConstructor"; returnfalse;
}
}
// Given we are calling a method, generate a frame. if (!EnsureHasFrame()) { returnfalse;
}
// Setup the arguments for the call.
uint32_t obj_reg = -1; constchar* shorty = dex_compilation_unit_.GetDexFile()->GetMethodShorty(method_index); if (opcode >= Instruction::INVOKE_VIRTUAL_RANGE) {
RangeInstructionOperands operands(instruction.VRegC(), instruction.VRegA_3rc()); if (!SetupArguments(invoke_type, operands, shorty, &obj_reg)) { returnfalse;
}
} else {
uint32_t args[5];
uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args);
VarArgsInstructionOperands operands(args, number_of_vreg_arguments); if (!SetupArguments(invoke_type, operands, shorty, &obj_reg)) { returnfalse;
}
} // Save the invoke return type for the next move-result instruction.
previous_invoke_return_type_ = DataType::FromShorty(shorty[0]);
if (invoke_type != kStatic) { bool can_be_null = CanBeNull(obj_reg); // Load the class of the instance. For kDirect and kSuper, this acts as a // null check. if (can_be_null || invoke_type == kVirtual || invoke_type == kInterface) {
InvokeDexCallingConvention calling_convention; Register receiver = calling_convention.GetRegisterAt(0);
Offset class_offset = mirror::Object::ClassOffset();
EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
__ Ldr(kArtMethodRegister.W(), HeapOperand(receiver.W(), class_offset)); if (can_be_null) {
UpdateNonNullMask(obj_reg, /* can_be_null= */ false);
RecordPcInfo(dex_pc);
}
}
}
Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize);
__ Ldr(lr, MemOperand(kArtMethodRegister, entry_point.SizeValue()));
{ // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
__ blr(lr);
RecordPcInfo(dex_pc);
} returntrue;
}
void FastCompilerARM64::InvokeRuntime(QuickEntrypointEnum entrypoint, uint32_t dex_pc) {
ThreadOffset64 entrypoint_offset = GetThreadOffset<kArm64PointerSize>(entrypoint);
__ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value())); // Ensure the pc position is recorded immediately after the `blr` instruction.
ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
__ blr(lr); if (EntrypointRequiresStackMap(entrypoint)) {
RecordPcInfo(dex_pc);
}
}
bool FastCompilerARM64::BuildLoadString(uint32_t vreg,
dex::StringIndex string_index, const Instruction* next) { // Generate a frame because of the read barrier. if (!EnsureHasFrame()) { returnfalse;
}
Location loc = CreateNewRegisterLocation(vreg, DataType::Type::kReference, next); if (HitUnimplemented()) { returnfalse;
}
bool FastCompilerARM64::BuildLoadClass(uint32_t vreg,
dex::TypeIndex type_index, const Instruction* next) { // Generate a frame because of the read barrier. if (!EnsureHasFrame()) { returnfalse;
}
Location loc = CreateNewRegisterLocation(vreg, DataType::Type::kReference, next); if (HitUnimplemented()) { returnfalse;
}
InvokeRuntimeCallingConvention calling_convention; Register cls = calling_convention.GetRegisterAt(1); // Use a temporary register for `obj_cls`. Cannot be a vixl temp as it needs // to survive a read barrier. Register obj_cls = calling_convention.GetRegisterAt(0); Register obj = WRegisterFrom(GetExistingRegisterLocation(vreg, DataType::Type::kReference));
Location result = CreateNewRegisterLocation(vreg_result, DataType::Type::kInt32, next); if (HitUnimplemented()) { returnfalse;
}
// Fetch the source before creating a new register for the destination, in // case they overlap.
Location source = vreg_locations_[src_reg];
// Translate a move into an actual move instruction. We could just update // `vreg_locations_`, but that would require tracking aliases, which may be // costly in compile time. if (!MoveLocation(CreateNewRegisterLocation(dest_reg, type, next), source, type)) { returnfalse;
} returntrue;
}
void FastCompilerARM64::SetIntConstant(uint32_t register_index,
int32_t constant, const Instruction* next) { bool can_be_object = (constant == 0); if (GetCodeItemAccessor().TriesSize() == 0 && !can_be_object) {
vreg_locations_[register_index] =
Location::ConstantLocation(new (allocator_) HIntConstant(constant));
} else { // In the presence of try/catch, we put the constant in a register directly. // This avoids having to dump dex register maps for stack maps, saving // compilation time. // We also store in a register for the constant zero to simplify object // register mask merging in the presence of control flow.
MoveLocation(CreateNewRegisterLocation(register_index, DataType::Type::kInt32, next),
Location::ConstantLocation(new (allocator_) HIntConstant(constant)),
DataType::Type::kInt32);
} // In case we branch, we need to make sure a null value can be merged // with an object value, so treat the 0 value as an object.
UpdateLocal(register_index, can_be_object, /* is_wide= */ false);
}
MemOperand mem = HeapOperand(array.W(), mirror::Array::LengthOffset().Uint32Value());
InvokeRuntimeCallingConvention calling_convention; Register temp = calling_convention.GetRegisterAt(1); // Fetch the length, and do a null pointer check.
{ // Ensure the pc position is recorded immediately after the load instruction.
EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
__ Ldr(temp, mem); if (CanBeNull(array_reg)) {
UpdateNonNullMask(array_reg, /* can_be_null= */ false);
RecordPcInfo(dex_pc);
}
}
Register index = RegisterFrom(GetExistingRegisterLocation(index_reg, DataType::Type::kInt32),
DataType::Type::kInt32);
if (can_receiver_be_null || is_object) { // We need a frame in case the null check throws or there is a read // barrier. if (!EnsureHasFrame()) { returnfalse;
}
}
MemOperand mem = HeapOperand(
RegisterFrom(GetExistingRegisterLocation(obj_reg, DataType::Type::kReference),
DataType::Type::kReference),
field->GetOffset()); if (HitUnimplemented()) { returnfalse;
} if (!DoGet(mem,
field_index,
instruction.Opcode(),
source_or_dest_reg,
can_receiver_be_null,
is_object,
field->IsVolatile(),
dex_pc,
next)) { returnfalse;
} // Update the information that the object on which we do the field access is // not null, unless its dex register aliases with the destination register. if (obj_reg != source_or_dest_reg) {
UpdateNonNullMask(obj_reg, /* can_be_null= */ false);
} returntrue;
}
if (is_long) {
__ Cmp(first.X(), second.X());
} else {
__ Fcmp(VRegister(first), VRegister(second));
}
__ Cset(dst.W(), ne); // result == +1 if NE or 0 otherwise
__ Cneg(dst.W(), dst.W(), is_gt_bias ? cc : lt); // result == -1 if LT or unchanged otherwise returntrue;
}
uint32_t array_reg = instruction.VRegA_31t(); Register array = RegisterFrom(
GetExistingRegisterLocation(array_reg, DataType::Type::kReference),
DataType::Type::kReference);
InvokeRuntimeCallingConvention calling_convention; Register length = calling_convention.GetRegisterAt(1).W();
MemOperand mem = HeapOperand(array.W(), mirror::Array::LengthOffset().Uint32Value()); // Fetch the length, and do a null pointer check.
{ // Ensure the pc position is recorded immediately after the load instruction.
EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
__ Ldr(length, mem); if (CanBeNull(array_reg)) {
UpdateNonNullMask(array_reg, /* can_be_null= */ false);
RecordPcInfo(dex_pc);
}
}
// Don't error on the stack size of `ProcessDexInstruction`, we know we are not // going to stack overflow in the compiler. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wframe-larger-than="
case Instruction::CONST_WIDE_16: {
int32_t register_index = instruction.VRegA_21s(); // Get 16 bits of constant value, sign extended to 64 bits.
int64_t value = instruction.VRegB_21s();
value <<= 48;
value >>= 48;
SetLongConstant(register_index, value, next); returntrue;
}
case Instruction::CONST_WIDE_32: {
int32_t register_index = instruction.VRegA_31i(); // Get 32 bits of constant value, sign extended to 64 bits.
int64_t value = instruction.VRegB_31i();
value <<= 32;
value >>= 32;
SetLongConstant(register_index, value, next); returntrue;
}
case Instruction::CONST_WIDE: {
int32_t register_index = instruction.VRegA_51l();
int64_t value = instruction.VRegB_51l();
SetLongConstant(register_index, value, next); returntrue;
}
case Instruction::CONST_WIDE_HIGH16: {
int32_t register_index = instruction.VRegA_21h();
int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48;
SetLongConstant(register_index, value, next); returntrue;
}
case Instruction::MOVE: { return BuildMove(
instruction.VRegA_12x(), instruction.VRegB_12x(), DataType::Type::kInt32, next);
} case Instruction::MOVE_FROM16: { return BuildMove(
instruction.VRegA_22x(), instruction.VRegB_22x(), DataType::Type::kInt32, next);
} case Instruction::MOVE_16: { return BuildMove(
instruction.VRegA_32x(), instruction.VRegB_32x(), DataType::Type::kInt32, next);
}
case Instruction::MOVE_WIDE: { return BuildMove(
instruction.VRegA_12x(), instruction.VRegB_12x(), DataType::Type::kInt64, next);
}
case Instruction::MOVE_WIDE_FROM16: { return BuildMove(
instruction.VRegA_22x(), instruction.VRegB_22x(), DataType::Type::kInt64, next);
}
case Instruction::MOVE_WIDE_16: { return BuildMove(
instruction.VRegA_32x(), instruction.VRegB_32x(), DataType::Type::kInt64, next);
}
case Instruction::MOVE_OBJECT: { return BuildMove(
instruction.VRegA_12x(), instruction.VRegB_12x(), DataType::Type::kReference, next);
} case Instruction::MOVE_OBJECT_FROM16: { return BuildMove(
instruction.VRegA_22x(), instruction.VRegB_22x(), DataType::Type::kReference, next);
} case Instruction::MOVE_OBJECT_16: { return BuildMove(
instruction.VRegA_32x(), instruction.VRegB_32x(), DataType::Type::kReference, next);
}
case Instruction::RETURN_VOID: { if (method_->IsConstructor() &&
!method_->IsStatic() &&
dex_compilation_unit_.RequiresConstructorBarrier()) {
__ Dmb(InnerShareable, BarrierWrites);
}
PopFrameAndReturn(); returntrue;
}
case Instruction::GOTO: case Instruction::GOTO_16: case Instruction::GOTO_32: { if (!EnsureHasFrame()) { returnfalse;
}
int32_t target_offset = instruction.GetTargetOffset(); if (target_offset <= 0) { if (!CanHandleBackwardsBranch(dex_pc + target_offset)) { returnfalse;
}
UseScratchRegisterScope temps(GetVIXLAssembler());
GenerateSuspendCheckAndIncrementHotness(dex_pc, temps.AcquireX());
}
PrepareToBranch(dex_pc + target_offset);
vixl::aarch64::Label* label = GetLabelOf(dex_pc + target_offset);
__ B(label); returntrue;
}
case Instruction::RETURN: case Instruction::RETURN_OBJECT: case Instruction::RETURN_WIDE: { return BuildReturn(instruction);
}
case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: return HandleInvoke(instruction, dex_pc, kDirect); case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: return HandleInvoke(instruction, dex_pc, kInterface); case Instruction::INVOKE_STATIC: case Instruction::INVOKE_STATIC_RANGE: return HandleInvoke(instruction, dex_pc, kStatic); case Instruction::INVOKE_SUPER: case Instruction::INVOKE_SUPER_RANGE: return HandleInvoke(instruction, dex_pc, kSuper); case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: { return HandleInvoke(instruction, dex_pc, kVirtual);
}
case Instruction::INVOKE_POLYMORPHIC: { break;
}
case Instruction::INVOKE_POLYMORPHIC_RANGE: { break;
}
case Instruction::INVOKE_CUSTOM: { break;
}
case Instruction::INVOKE_CUSTOM_RANGE: { break;
}
case Instruction::NEG_FLOAT: {
SETUP_UNOP_12x(DataType::Type::kFloat32, DataType::Type::kFloat32)
__ Fneg(dst.S(), input.S()); returntrue;
}
case Instruction::NEG_DOUBLE: {
SETUP_UNOP_12x(DataType::Type::kFloat64, DataType::Type::kFloat64)
__ Fneg(dst.D(), input.D()); returntrue;
}
case Instruction::NEG_INT: {
SETUP_UNOP_12x(DataType::Type::kInt32, DataType::Type::kInt32)
__ Neg(dst.W(), input.W()); returntrue;
}
case Instruction::NEG_LONG: {
SETUP_UNOP_12x(DataType::Type::kInt64, DataType::Type::kInt64)
__ Neg(dst.X(), input.X()); returntrue;
}
case Instruction::NOT_INT: {
SETUP_UNOP_12x(DataType::Type::kInt32, DataType::Type::kInt32)
__ Mvn(dst.W(), input.W()); returntrue;
}
case Instruction::NOT_LONG: {
SETUP_UNOP_12x(DataType::Type::kInt64, DataType::Type::kInt64)
__ Mvn(dst.X(), input.X()); returntrue;
}
case Instruction::FILL_ARRAY_DATA: { return BuildFillArrayData(instruction, dex_pc);
}
case Instruction::MOVE_RESULT_OBJECT:
is_object = true;
FALLTHROUGH_INTENDED; case Instruction::MOVE_RESULT: case Instruction::MOVE_RESULT_WIDE: { return BuildMoveResult(instruction, is_object, next);
}
case Instruction::CMP_LONG: case Instruction::CMPG_DOUBLE: case Instruction::CMPL_DOUBLE: case Instruction::CMPG_FLOAT: case Instruction::CMPL_FLOAT: { return BuildCompare(instruction, next);
}
case Instruction::NOP: returntrue;
case Instruction::IGET_OBJECT:
is_object = true;
FALLTHROUGH_INTENDED; case Instruction::IGET: case Instruction::IGET_WIDE: case Instruction::IGET_BOOLEAN: case Instruction::IGET_BYTE: case Instruction::IGET_CHAR: case Instruction::IGET_SHORT: { return BuildInstanceFieldGet(instruction, dex_pc, is_object, next);
}
case Instruction::IPUT_OBJECT:
is_object = true;
FALLTHROUGH_INTENDED; case Instruction::IPUT: case Instruction::IPUT_WIDE: case Instruction::IPUT_BOOLEAN: case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { return BuildInstanceFieldSet(instruction, dex_pc, is_object);
}
case Instruction::SGET_OBJECT:
is_object = true;
FALLTHROUGH_INTENDED; case Instruction::SGET: case Instruction::SGET_WIDE: case Instruction::SGET_BOOLEAN: case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: { return BuildStaticFieldAccess(instruction, dex_pc, is_object, /* is_put= */ false, next);
}
case Instruction::SPUT_OBJECT:
is_object = true;
FALLTHROUGH_INTENDED; case Instruction::SPUT: case Instruction::SPUT_WIDE: case Instruction::SPUT_BOOLEAN: case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: { return BuildStaticFieldAccess(instruction, dex_pc, is_object, /* is_put= */ true, next);
}
case Instruction::MONITOR_ENTER: { // We can start collecting vreg info per stack map at this point, as the // runtime will only start expecting them after getting a report of an // Object in a dex register being locked. For any stack maps before that // monitor-enter, we know the runtime won't expect it. // Note: once we support backwards branching, we'll need to know // beforehand if a method has monitor operations.
needs_vreg_info_ = true; return BuildInvokeRuntime11x(kQuickLockObject, instruction, dex_pc);
}
case Instruction::MONITOR_EXIT: { // We don't support backwards branch yet, so we must have seen the // monitor-enter before this instruction.
DCHECK(needs_vreg_info_); return BuildInvokeRuntime11x(kQuickUnlockObject, instruction, dex_pc);
}
case Instruction::SPARSE_SWITCH: case Instruction::PACKED_SWITCH: { return BuildSwitch(instruction, dex_pc);
}
case Instruction::UNUSED_3E ... Instruction::UNUSED_43: case Instruction::UNUSED_73: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: case Instruction::UNUSED_E3 ... Instruction::UNUSED_F9: { break;
}
}
unimplemented_reason_ = instruction.Name(); returnfalse;
} // NOLINT(readability/fn_size)
// The existing masks have been updated with parameter information. // Save them and restore them after this analysis.
uint64_t saved_object_register_mask = object_register_mask_;
ArenaBitVector saved_object_stack_mask(allocator_, /*start_bits=*/ 0, /*expandable=*/ true);
saved_object_stack_mask.Copy(&object_stack_mask_);
// If the instruction can throw, emulate a branch to each catch handler. if (ThrowsIntoCatchHandler(instruction)) { const dex::TryItem* try_item = GetCodeItemAccessor().FindTryItem(pair.DexPc()); if (try_item != nullptr) { for (CatchHandlerIterator iterator(GetCodeItemAccessor(), *try_item);
iterator.HasNext();
iterator.Next()) { if (UpdateObjectMasks(iterator.GetHandlerAddress())) {
AddToWorkQueue(iterator.GetHandlerAddress());
}
}
}
}
++it; if (it == end || !instruction.CanFlowThrough()) { break;
}
dex_pc = (*it).DexPc(); if (IsBranchTarget(dex_pc)) { if (UpdateObjectMasks(dex_pc)) {
AddToWorkQueue(dex_pc);
} break;
}
} while (true); returntrue;
}
bool FastCompilerARM64::ProcessDexInstructionForMasks(const Instruction& instruction) { switch (instruction.Opcode()) { case Instruction::CONST_4: case Instruction::CONST_16: case Instruction::CONST: case Instruction::CONST_HIGH16: { bool can_be_object = (instruction.VRegB() == 0);
UpdateLocal(instruction.VRegA(), can_be_object, /* is_wide= */ false); returntrue;
}
#define IF_XX(cond) \ case Instruction::IF_##cond: \ case Instruction::IF_##cond##Z:
IF_XX(EQ)
IF_XX(NE)
IF_XX(LT)
IF_XX(LE)
IF_XX(GT)
IF_XX(GE) #undef IF_XX case Instruction::GOTO: case Instruction::GOTO_16: case Instruction::GOTO_32: case Instruction::RETURN: case Instruction::RETURN_OBJECT: case Instruction::RETURN_WIDE: case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: case Instruction::INVOKE_STATIC: case Instruction::INVOKE_STATIC_RANGE: case Instruction::INVOKE_SUPER: case Instruction::INVOKE_SUPER_RANGE: case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: case Instruction::INVOKE_POLYMORPHIC: case Instruction::INVOKE_POLYMORPHIC_RANGE: case Instruction::INVOKE_CUSTOM: case Instruction::INVOKE_CUSTOM_RANGE: case Instruction::RETURN_VOID: case Instruction::NOP: case Instruction::IPUT_OBJECT: case Instruction::IPUT: case Instruction::IPUT_BOOLEAN: case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: case Instruction::IPUT_WIDE: case Instruction::SPUT: case Instruction::SPUT_BOOLEAN: case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: case Instruction::SPUT_WIDE: case Instruction::APUT_WIDE: case Instruction::APUT_OBJECT: case Instruction::SPUT_OBJECT: case Instruction::APUT: case Instruction::APUT_BOOLEAN: case Instruction::APUT_BYTE: case Instruction::APUT_CHAR: case Instruction::APUT_SHORT: case Instruction::THROW: case Instruction::CHECK_CAST: case Instruction::MONITOR_ENTER: case Instruction::MONITOR_EXIT: case Instruction::SPARSE_SWITCH: case Instruction::PACKED_SWITCH: case Instruction::FILLED_NEW_ARRAY: case Instruction::FILLED_NEW_ARRAY_RANGE: case Instruction::FILL_ARRAY_DATA: { returntrue;
}
#define OP_CASE(opcode) \ case Instruction::opcode ##_INT_2ADDR: \ case Instruction::opcode ##_INT:
OP_CASE(ADD)
OP_CASE(SUB)
OP_CASE(MUL)
OP_CASE(AND)
OP_CASE(OR)
OP_CASE(XOR)
OP_CASE(DIV)
OP_CASE(REM)
OP_CASE(SHL)
OP_CASE(SHR)
OP_CASE(USHR) #undef OP_CASE case Instruction::CMP_LONG: case Instruction::CMPG_DOUBLE: case Instruction::CMPL_DOUBLE: case Instruction::CMPG_FLOAT: case Instruction::CMPL_FLOAT: case Instruction::NEG_INT: case Instruction::NEG_FLOAT: case Instruction::NOT_INT: case Instruction::LONG_TO_INT: case Instruction::INT_TO_BYTE: case Instruction::INT_TO_SHORT: case Instruction::INT_TO_CHAR: case Instruction::INT_TO_FLOAT: case Instruction::LONG_TO_FLOAT: case Instruction::FLOAT_TO_INT: case Instruction::DOUBLE_TO_INT: case Instruction::DOUBLE_TO_FLOAT: case Instruction::REM_FLOAT: case Instruction::REM_FLOAT_2ADDR: case Instruction::ADD_FLOAT_2ADDR: case Instruction::ADD_FLOAT: case Instruction::SUB_FLOAT_2ADDR: case Instruction::SUB_FLOAT: case Instruction::MUL_FLOAT_2ADDR: case Instruction::MUL_FLOAT: case Instruction::DIV_FLOAT_2ADDR: case Instruction::DIV_FLOAT: case Instruction::ADD_INT_LIT16: case Instruction::AND_INT_LIT16: case Instruction::OR_INT_LIT16: case Instruction::XOR_INT_LIT16: case Instruction::MUL_INT_LIT16: case Instruction::DIV_INT_LIT16: case Instruction::RSUB_INT: case Instruction::REM_INT_LIT16: case Instruction::ADD_INT_LIT8: case Instruction::AND_INT_LIT8: case Instruction::OR_INT_LIT8: case Instruction::XOR_INT_LIT8: case Instruction::RSUB_INT_LIT8: case Instruction::MUL_INT_LIT8: case Instruction::DIV_INT_LIT8: case Instruction::REM_INT_LIT8: case Instruction::SHL_INT_LIT8: case Instruction::SHR_INT_LIT8: case Instruction::USHR_INT_LIT8: case Instruction::MOVE_RESULT: case Instruction::IGET: case Instruction::IGET_BOOLEAN: case Instruction::IGET_BYTE: case Instruction::IGET_CHAR: case Instruction::IGET_SHORT: case Instruction::SGET: case Instruction::SGET_BOOLEAN: case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: case Instruction::ARRAY_LENGTH: case Instruction::AGET: case Instruction::AGET_BOOLEAN: case Instruction::AGET_BYTE: case Instruction::AGET_CHAR: case Instruction::AGET_SHORT: case Instruction::MOVE: case Instruction::MOVE_16: case Instruction::MOVE_FROM16: case Instruction::INSTANCE_OF: {
int32_t vreg_a = instruction.VRegA();
UpdateLocal(vreg_a, /* is_object= */ false, /* is_wide= */ false); returntrue;
}
#define OP_CASE(opcode) \ case Instruction::opcode ##_LONG_2ADDR: \ case Instruction::opcode ##_LONG:
OP_CASE(ADD)
OP_CASE(SUB)
OP_CASE(MUL)
OP_CASE(AND)
OP_CASE(OR)
OP_CASE(XOR)
OP_CASE(DIV)
OP_CASE(REM)
OP_CASE(SHL)
OP_CASE(SHR)
OP_CASE(USHR) #undef OP_CASE case Instruction::NEG_DOUBLE: case Instruction::NEG_LONG: case Instruction::NOT_LONG: case Instruction::INT_TO_LONG: case Instruction::INT_TO_DOUBLE: case Instruction::LONG_TO_DOUBLE: case Instruction::FLOAT_TO_LONG: case Instruction::FLOAT_TO_DOUBLE: case Instruction::DOUBLE_TO_LONG: case Instruction::REM_DOUBLE: case Instruction::REM_DOUBLE_2ADDR: case Instruction::ADD_DOUBLE_2ADDR: case Instruction::ADD_DOUBLE: case Instruction::SUB_DOUBLE_2ADDR: case Instruction::SUB_DOUBLE: case Instruction::MUL_DOUBLE_2ADDR: case Instruction::MUL_DOUBLE: case Instruction::DIV_DOUBLE_2ADDR: case Instruction::DIV_DOUBLE: case Instruction::MOVE_RESULT_WIDE: case Instruction::SGET_WIDE: case Instruction::IGET_WIDE: case Instruction::AGET_WIDE: case Instruction::CONST_WIDE_16: case Instruction::CONST_WIDE_32: case Instruction::CONST_WIDE: case Instruction::CONST_WIDE_HIGH16: case Instruction::MOVE_WIDE: case Instruction::MOVE_WIDE_FROM16: case Instruction::MOVE_WIDE_16: {
int32_t vreg_a = instruction.VRegA();
UpdateLocal(vreg_a, /* is_object= */ false, /* is_wide= */ true); returntrue;
}
case Instruction::NEW_ARRAY: case Instruction::NEW_INSTANCE: case Instruction::MOVE_RESULT_OBJECT: case Instruction::IGET_OBJECT: case Instruction::SGET_OBJECT: case Instruction::AGET_OBJECT: case Instruction::CONST_STRING: case Instruction::CONST_STRING_JUMBO: case Instruction::CONST_CLASS: case Instruction::CONST_METHOD_HANDLE: case Instruction::CONST_METHOD_TYPE: case Instruction::MOVE_EXCEPTION: case Instruction::MOVE_OBJECT: case Instruction::MOVE_OBJECT_FROM16: case Instruction::MOVE_OBJECT_16: {
UpdateLocal(instruction.VRegA(), /* is_object= */ true, /* is_wide= */ false); returntrue;
}
case Instruction::UNUSED_3E ... Instruction::UNUSED_43: case Instruction::UNUSED_73: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: case Instruction::UNUSED_E3 ... Instruction::UNUSED_F9: { break;
}
}
unimplemented_reason_ = instruction.Name(); returnfalse;
}
bool FastCompilerARM64::Compile(bool with_loop_support) { if (!InitializeParameters()) {
DCHECK(HitUnimplemented());
AbortCompilation(); returnfalse;
} if (with_loop_support) { if (!EnsureHasFrame()) {
AbortCompilation(); returnfalse;
}
AnalyzeBranches();
}
if (!ProcessInstructions()) {
DCHECK(HitUnimplemented());
AbortCompilation(); returnfalse;
}
DCHECK(!HitUnimplemented()) << GetUnimplementedReason();
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.