diff options
Diffstat (limited to 'compiler/optimizing/instruction_builder.cc')
-rw-r--r-- | compiler/optimizing/instruction_builder.cc | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 4364d39263..8ec28e0ab1 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1168,6 +1168,149 @@ static bool VarHandleAccessorNeedsReturnTypeCheck(HInvoke* invoke, DataType::Typ } } +// This function initializes `VarHandleOptimizations`, does a number of static checks and disables +// the intrinsic if some of the checks fail. This is necessary for the code generator to work (for +// both the baseline and the optimizing compiler). +static void DecideVarHandleIntrinsic(HInvoke* invoke) { + switch (invoke->GetIntrinsic()) { + case Intrinsics::kVarHandleCompareAndExchange: + case Intrinsics::kVarHandleCompareAndExchangeAcquire: + case Intrinsics::kVarHandleCompareAndExchangeRelease: + case Intrinsics::kVarHandleCompareAndSet: + case Intrinsics::kVarHandleGet: + case Intrinsics::kVarHandleGetAcquire: + case Intrinsics::kVarHandleGetAndAdd: + case Intrinsics::kVarHandleGetAndAddAcquire: + case Intrinsics::kVarHandleGetAndAddRelease: + case Intrinsics::kVarHandleGetAndBitwiseAnd: + case Intrinsics::kVarHandleGetAndBitwiseAndAcquire: + case Intrinsics::kVarHandleGetAndBitwiseAndRelease: + case Intrinsics::kVarHandleGetAndBitwiseOr: + case Intrinsics::kVarHandleGetAndBitwiseOrAcquire: + case Intrinsics::kVarHandleGetAndBitwiseOrRelease: + case Intrinsics::kVarHandleGetAndBitwiseXor: + case Intrinsics::kVarHandleGetAndBitwiseXorAcquire: + case Intrinsics::kVarHandleGetAndBitwiseXorRelease: + case Intrinsics::kVarHandleGetAndSet: + case Intrinsics::kVarHandleGetAndSetAcquire: + case Intrinsics::kVarHandleGetAndSetRelease: + case Intrinsics::kVarHandleGetOpaque: + case Intrinsics::kVarHandleGetVolatile: + case Intrinsics::kVarHandleSet: + case Intrinsics::kVarHandleSetOpaque: + case Intrinsics::kVarHandleSetRelease: + case Intrinsics::kVarHandleSetVolatile: + case Intrinsics::kVarHandleWeakCompareAndSet: + case Intrinsics::kVarHandleWeakCompareAndSetAcquire: + case Intrinsics::kVarHandleWeakCompareAndSetPlain: + case Intrinsics::kVarHandleWeakCompareAndSetRelease: + break; + default: + return; // Not a VarHandle intrinsic, skip. + } + + DCHECK(invoke->IsInvokePolymorphic()); + VarHandleOptimizations optimizations(invoke); + + // Do only simple static checks here (those for which we have enough information). More complex + // checks should be done in instruction simplifier, which runs after other optimization passes + // that may provide useful information. + + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + if (expected_coordinates_count > 2u) { + optimizations.SetDoNotIntrinsify(); + return; + } + if (expected_coordinates_count != 0u) { + // Except for static fields (no coordinates), the first coordinate must be a reference. + // Do not intrinsify if the reference is null as we would always go to slow path anyway. + HInstruction* object = invoke->InputAt(1); + if (object->GetType() != DataType::Type::kReference || object->IsNullConstant()) { + optimizations.SetDoNotIntrinsify(); + return; + } + } + if (expected_coordinates_count == 2u) { + // For arrays and views, the second coordinate must be convertible to `int`. + // In this context, `boolean` is not convertible but we have to look at the shorty + // as compiler transformations can give the invoke a valid boolean input. + DataType::Type index_type = GetDataTypeFromShorty(invoke, 2); + if (index_type == DataType::Type::kBool || + DataType::Kind(index_type) != DataType::Type::kInt32) { + optimizations.SetDoNotIntrinsify(); + return; + } + } + + uint32_t number_of_arguments = invoke->GetNumberOfArguments(); + DataType::Type return_type = invoke->GetType(); + mirror::VarHandle::AccessModeTemplate access_mode_template = + mirror::VarHandle::GetAccessModeTemplateByIntrinsic(invoke->GetIntrinsic()); + switch (access_mode_template) { + case mirror::VarHandle::AccessModeTemplate::kGet: + // The return type should be the same as varType, so it shouldn't be void. + if (return_type == DataType::Type::kVoid) { + optimizations.SetDoNotIntrinsify(); + return; + } + break; + case mirror::VarHandle::AccessModeTemplate::kSet: + if (return_type != DataType::Type::kVoid) { + optimizations.SetDoNotIntrinsify(); + return; + } + break; + case mirror::VarHandle::AccessModeTemplate::kCompareAndSet: { + if (return_type != DataType::Type::kBool) { + optimizations.SetDoNotIntrinsify(); + return; + } + uint32_t expected_value_index = number_of_arguments - 2; + uint32_t new_value_index = number_of_arguments - 1; + DataType::Type expected_value_type = GetDataTypeFromShorty(invoke, expected_value_index); + DataType::Type new_value_type = GetDataTypeFromShorty(invoke, new_value_index); + if (expected_value_type != new_value_type) { + optimizations.SetDoNotIntrinsify(); + return; + } + break; + } + case mirror::VarHandle::AccessModeTemplate::kCompareAndExchange: { + uint32_t expected_value_index = number_of_arguments - 2; + uint32_t new_value_index = number_of_arguments - 1; + DataType::Type expected_value_type = GetDataTypeFromShorty(invoke, expected_value_index); + DataType::Type new_value_type = GetDataTypeFromShorty(invoke, new_value_index); + if (expected_value_type != new_value_type || return_type != expected_value_type) { + optimizations.SetDoNotIntrinsify(); + return; + } + break; + } + case mirror::VarHandle::AccessModeTemplate::kGetAndUpdate: { + DataType::Type value_type = GetDataTypeFromShorty(invoke, number_of_arguments - 1); + if (IsVarHandleGetAndAdd(invoke) && + (value_type == DataType::Type::kReference || value_type == DataType::Type::kBool)) { + // We should only add numerical types. + optimizations.SetDoNotIntrinsify(); + return; + } else if (IsVarHandleGetAndBitwiseOp(invoke) && !DataType::IsIntegralType(value_type)) { + // We can only apply operators to bitwise integral types. + // Note that bitwise VarHandle operations accept a non-integral boolean type and + // perform the appropriate logical operation. However, the result is the same as + // using the bitwise operation on our boolean representation and this fits well + // with DataType::IsIntegralType() treating the compiler type kBool as integral. + optimizations.SetDoNotIntrinsify(); + return; + } + if (value_type != return_type) { + optimizations.SetDoNotIntrinsify(); + return; + } + break; + } + } +} + bool HInstructionBuilder::BuildInvokePolymorphic(uint32_t dex_pc, uint32_t method_idx, dex::ProtoIndex proto_idx, @@ -1217,6 +1360,8 @@ bool HInstructionBuilder::BuildInvokePolymorphic(uint32_t dex_pc, latest_result_ = current_block_->GetLastInstruction(); } + DecideVarHandleIntrinsic(invoke); + return true; } |