diff options
-rw-r--r-- | compiler/optimizing/intrinsics_arm64.cc | 5 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_riscv64.cc | 3 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_x86.cc | 18 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_x86_64.cc | 109 | ||||
-rw-r--r-- | test/859-checker-var-handles-intrinsics/info.txt | 4 |
5 files changed, 89 insertions, 50 deletions
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 4af63fa906..851b6e96ac 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -5437,10 +5437,8 @@ static void CreateVarHandleGetAndUpdateLocations(HInvoke* invoke, } // Get the type from the shorty as the invokes may not return a value. - LocationSummary* locations = CreateVarHandleCommonLocations(invoke, codegen); uint32_t arg_index = invoke->GetNumberOfArguments() - 1; DataType::Type value_type = GetDataTypeFromShorty(invoke, arg_index); - size_t old_temp_count = locations->GetTempCount(); if (value_type == DataType::Type::kReference && codegen->EmitNonBakerReadBarrier()) { // Unsupported for non-Baker read barrier because the artReadBarrierSlow() ignores // the passed reference and reloads it from the field, thus seeing the new value @@ -5448,6 +5446,9 @@ static void CreateVarHandleGetAndUpdateLocations(HInvoke* invoke, return; } + LocationSummary* locations = CreateVarHandleCommonLocations(invoke, codegen); + size_t old_temp_count = locations->GetTempCount(); + DCHECK_EQ(old_temp_count, (GetExpectedVarHandleCoordinatesCount(invoke) == 0) ? 2u : 1u); if (DataType::IsFloatingPointType(value_type)) { if (get_and_update_op == GetAndUpdateOp::kAdd) { diff --git a/compiler/optimizing/intrinsics_riscv64.cc b/compiler/optimizing/intrinsics_riscv64.cc index 4e5dbe4d23..f705aa2a60 100644 --- a/compiler/optimizing/intrinsics_riscv64.cc +++ b/compiler/optimizing/intrinsics_riscv64.cc @@ -4466,8 +4466,8 @@ static void CreateVarHandleGetAndUpdateLocations(HInvoke* invoke, } // Get the type from the shorty as the invokes may not return a value. - LocationSummary* locations = CreateVarHandleCommonLocations(invoke, codegen); uint32_t arg_index = invoke->GetNumberOfArguments() - 1; + DCHECK_EQ(arg_index, 1u + GetExpectedVarHandleCoordinatesCount(invoke)); DataType::Type value_type = GetDataTypeFromShorty(invoke, arg_index); if (value_type == DataType::Type::kReference && codegen->EmitNonBakerReadBarrier()) { // Unsupported for non-Baker read barrier because the artReadBarrierSlow() ignores @@ -4481,6 +4481,7 @@ static void CreateVarHandleGetAndUpdateLocations(HInvoke* invoke, return; } + LocationSummary* locations = CreateVarHandleCommonLocations(invoke, codegen); Location arg = locations->InAt(arg_index); bool is_fp = DataType::IsFloatingPointType(value_type); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index dc88de474c..c1ec8bda82 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2517,8 +2517,10 @@ void CreateUnsafeGetAndUpdateLocations(ArenaAllocator* allocator, // to take advantage of XCHG or XADD. Arbitrarily pick EAX. locations->SetInAt(3, Location::RegisterLocation(EAX)); // Only set the `out` register if it's needed. In the void case we can still use EAX in the - // same manner as it is used an as `in` register. - if (!is_void) { + // same manner as it is marked as a temp register. + if (is_void) { + locations->AddTemp(Location::RegisterLocation(EAX)); + } else { locations->SetOut(Location::RegisterLocation(EAX)); } } @@ -4296,8 +4298,10 @@ static void CreateVarHandleGetAndSetLocations(HInvoke* invoke, CodeGeneratorX86* } else { locations->SetInAt(value_index, Location::RegisterLocation(EAX)); // Only set the `out` register if it's needed. In the void case we can still use EAX in the - // same manner as it is used an as `in` register. - if (!is_void) { + // same manner as it is marked as a temp register. + if (is_void) { + locations->AddTemp(Location::RegisterLocation(EAX)); + } else { locations->SetOut(Location::RegisterLocation(EAX)); } } @@ -4680,8 +4684,10 @@ static void CreateVarHandleGetAndAddLocations(HInvoke* invoke, CodeGeneratorX86* // xadd updates the register argument with the old value. ByteRegister required for xaddb. locations->SetInAt(value_index, Location::RegisterLocation(EAX)); // Only set the `out` register if it's needed. In the void case we can still use EAX in the - // same manner as it is used an as `in` register. - if (!is_void) { + // same manner as it is marked as a temp register. + if (is_void) { + locations->AddTemp(Location::RegisterLocation(EAX)); + } else { locations->SetOut(Location::RegisterLocation(EAX)); } } diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index ba254ee705..a8e7bf4881 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2635,8 +2635,10 @@ static void CreateUnsafeGetAndUpdateLocations(ArenaAllocator* allocator, // to take advantage of XCHG or XADD. Arbitrarily pick RAX. locations->SetInAt(3, Location::RegisterLocation(RAX)); // Only set the `out` register if it's needed. In the void case we can still use RAX in the - // same manner as it is used an as `in` register. - if (invoke->GetType() != DataType::Type::kVoid) { + // same manner as it is marked as a temp register. + if (invoke->GetType() == DataType::Type::kVoid) { + locations->AddTemp(Location::RegisterLocation(RAX)); + } else { locations->SetOut(Location::RegisterLocation(RAX)); } } @@ -2729,9 +2731,16 @@ static void GenUnsafeGetAndUpdate(HInvoke* invoke, } else { DCHECK_EQ(type, DataType::Type::kReference); DCHECK(get_and_update_op == GetAndUpdateOp::kSet); - CpuRegister temp1 = locations->GetTemp(0).AsRegister<CpuRegister>(); - CpuRegister temp2 = locations->GetTemp(1).AsRegister<CpuRegister>(); - CpuRegister temp3 = locations->GetTemp(2).AsRegister<CpuRegister>(); + + // In the void case, we have an extra temp register, which is used to signal the register + // allocator that we are clobering RAX. + const uint32_t extra_temp = is_void ? 1u : 0u; + DCHECK_EQ(locations->GetTempCount(), 3u + extra_temp); + DCHECK_IMPLIES(is_void, locations->GetTemp(0u).Equals(Location::RegisterLocation(RAX))); + + CpuRegister temp1 = locations->GetTemp(0u + extra_temp).AsRegister<CpuRegister>(); + CpuRegister temp2 = locations->GetTemp(1u + extra_temp).AsRegister<CpuRegister>(); + CpuRegister temp3 = locations->GetTemp(2u + extra_temp).AsRegister<CpuRegister>(); if (codegen->EmitReadBarrier()) { DCHECK(kUseBakerReadBarrier); @@ -4516,13 +4525,6 @@ static void CreateVarHandleGetAndSetLocations(HInvoke* invoke, CodeGeneratorX86_ // A temporary is needed to load the new floating-point value into a register for XCHG. locations->AddTemp(Location::RequiresRegister()); } else { - // Only set the `out` register if it's needed. In the void case we can still use RAX in the - // same manner as it is used an as `in` register. - if (!is_void) { - // Use the same register for both the new value and output to take advantage of XCHG. - // It doesn't have to be RAX, but we need to choose some to make sure it's the same. - locations->SetOut(Location::RegisterLocation(RAX)); - } locations->SetInAt(new_value_index, Location::RegisterLocation(RAX)); if (value_type == DataType::Type::kReference) { // Need two temporaries for MarkGCCard. @@ -4533,6 +4535,15 @@ static void CreateVarHandleGetAndSetLocations(HInvoke* invoke, CodeGeneratorX86_ locations->AddTemp(Location::RequiresRegister()); } } + // Only set the `out` register if it's needed. In the void case we can still use RAX in the + // same manner as it is marked as a temp register. + if (is_void) { + locations->AddTemp(Location::RegisterLocation(RAX)); + } else { + // Use the same register for both the new value and output to take advantage of XCHG. + // It doesn't have to be RAX, but we need to choose some to make sure it's the same. + locations->SetOut(Location::RegisterLocation(RAX)); + } } } @@ -4577,18 +4588,24 @@ static void GenerateVarHandleGetAndSet(HInvoke* invoke, // Output register is the same as the one holding new value, so no need to move the result. DCHECK(!byte_swap); - CpuRegister temp1 = locations->GetTemp(temp_count - 1).AsRegister<CpuRegister>(); - CpuRegister temp2 = locations->GetTemp(temp_count - 2).AsRegister<CpuRegister>(); + // In the void case, we have an extra temp register, which is used to signal the register + // allocator that we are clobering RAX. + const uint32_t extra_temp = is_void ? 1u : 0u; + DCHECK_IMPLIES(is_void, + locations->GetTemp(temp_count - 1u).Equals(Location::RegisterLocation(RAX))); + + CpuRegister temp1 = locations->GetTemp(temp_count - extra_temp - 1u).AsRegister<CpuRegister>(); + CpuRegister temp2 = locations->GetTemp(temp_count - extra_temp - 2u).AsRegister<CpuRegister>(); CpuRegister valreg = value.AsRegister<CpuRegister>(); if (codegen->EmitBakerReadBarrier()) { codegen->GenerateReferenceLoadWithBakerReadBarrier( invoke, - locations->GetTemp(temp_count - 3), + locations->GetTemp(temp_count - extra_temp - 3u), ref, field_addr, - /*needs_null_check=*/ false, - /*always_update_field=*/ true, + /*needs_null_check=*/false, + /*always_update_field=*/true, &temp1, &temp2); } @@ -4677,16 +4694,6 @@ static void CreateVarHandleGetAndBitwiseOpLocations(HInvoke* invoke, CodeGenerat DCHECK_NE(DataType::Type::kReference, value_type); DCHECK(!DataType::IsFloatingPointType(value_type)); - - if (!is_void) { - // Output is in RAX to accommodate CMPXCHG. It is also used as a temporary. - locations->SetOut(Location::RegisterLocation(RAX)); - } else { - // Used as a temporary, even when we are not outputting it so reserve it. This has to be - // requested before the other temporary since there's variable number of temp registers and the - // other temp register is expected to be the last one. - locations->AddTemp(Location::RegisterLocation(RAX)); - } // A temporary to compute the bitwise operation on the old and the new values. locations->AddTemp(Location::RequiresRegister()); // We need value to be either in a register, or a 32-bit constant (as there are no arithmetic @@ -4695,6 +4702,15 @@ static void CreateVarHandleGetAndBitwiseOpLocations(HInvoke* invoke, CodeGenerat DataType::Is64BitType(value_type) ? Location::RequiresRegister() : Location::RegisterOrConstant(invoke->InputAt(new_value_index))); + if (is_void) { + // Used as a temporary, even when we are not outputting it so reserve it. This has to be + // requested before the other temporary since there's variable number of temp registers and the + // other temp register is expected to be the last one. + locations->AddTemp(Location::RegisterLocation(RAX)); + } else { + // Output is in RAX to accommodate CMPXCHG. It is also used as a temporary. + locations->SetOut(Location::RegisterLocation(RAX)); + } } static void GenerateVarHandleGetAndOp(HInvoke* invoke, @@ -4706,19 +4722,25 @@ static void GenerateVarHandleGetAndOp(HInvoke* invoke, bool byte_swap) { X86_64Assembler* assembler = codegen->GetAssembler(); LocationSummary* locations = invoke->GetLocations(); - Location temp_loc = locations->GetTemp(locations->GetTempCount() - 1u); + // In the void case, we have an extra temp register, which is used to signal the register + // allocator that we are clobering RAX. + const bool is_void = invoke->GetType() == DataType::Type::kVoid; + const uint32_t extra_temp = is_void ? 1u : 0u; + const uint32_t temp_count = locations->GetTempCount(); + DCHECK_IMPLIES(is_void, + locations->GetTemp(temp_count - 1u).Equals(Location::RegisterLocation(RAX))); + Location temp_loc = locations->GetTemp(temp_count - extra_temp - 1u); Location rax_loc = Location::RegisterLocation(RAX); - DCHECK_IMPLIES(invoke->GetType() != DataType::Type::kVoid, locations->Out().Equals(rax_loc)); + DCHECK_IMPLIES(!is_void, locations->Out().Equals(rax_loc)); CpuRegister temp = temp_loc.AsRegister<CpuRegister>(); CpuRegister rax = rax_loc.AsRegister<CpuRegister>(); - DCHECK_EQ(rax.AsRegister(), RAX); bool is64Bit = DataType::Is64BitType(type); NearLabel retry; __ Bind(&retry); // Load field value into RAX and copy it into a temporary register for the operation. - codegen->LoadFromMemoryNoReference(type, Location::RegisterLocation(RAX), field_addr); + codegen->LoadFromMemoryNoReference(type, rax_loc, field_addr); codegen->Move(temp_loc, rax_loc); if (byte_swap) { // Byte swap the temporary, since we need to perform operation in native endianness. @@ -4841,14 +4863,6 @@ static void CreateVarHandleGetAndAddLocations(HInvoke* invoke, CodeGeneratorX86_ locations->AddTemp(Location::RequiresRegister()); } else { DCHECK_NE(value_type, DataType::Type::kReference); - // Only set the `out` register if it's needed. In the void case we can still use RAX in the - // same manner as it is used an as `in` register. - if (!is_void) { - // Use the same register for both the new value and output to take advantage of XADD. - // It should be RAX, because the byte-swapping path of GenerateVarHandleGetAndAdd falls - // back to GenerateVarHandleGetAndOp that expects out in RAX. - locations->SetOut(Location::RegisterLocation(RAX)); - } locations->SetInAt(new_value_index, Location::RegisterLocation(RAX)); if (GetExpectedVarHandleCoordinatesCount(invoke) == 2) { // For byte array views with non-native endianness we need extra BSWAP operations, so we @@ -4858,6 +4872,16 @@ static void CreateVarHandleGetAndAddLocations(HInvoke* invoke, CodeGeneratorX86_ // cannot distinguish this case from arrays or native-endian byte array views. locations->AddRegisterTemps(2); } + // Only set the `out` register if it's needed. In the void case we can still use RAX in the + // same manner as it is marked as a temp register. + if (is_void) { + locations->AddTemp(Location::RegisterLocation(RAX)); + } else { + // Use the same register for both the new value and output to take advantage of XADD. + // It should be RAX, because the byte-swapping path of GenerateVarHandleGetAndAdd falls + // back to GenerateVarHandleGetAndOp that expects out in RAX. + locations->SetOut(Location::RegisterLocation(RAX)); + } } } @@ -4948,7 +4972,12 @@ static void GenerateVarHandleGetAndAdd(HInvoke* invoke, // implementation that is also used for bitwise operations. // Move value from RAX to a temporary register, as RAX may get clobbered by repeated CMPXCHG. DCHECK_EQ(GetExpectedVarHandleCoordinatesCount(invoke), 2u); - Location temp = locations->GetTemp(temp_count - 2); + // In the void case, we have an extra temp register, which is used to signal the register + // allocator that we are clobering RAX. + const uint32_t extra_temp = is_void ? 1u : 0u; + DCHECK_IMPLIES(is_void, + locations->GetTemp(temp_count - 1u).Equals(Location::RegisterLocation(RAX))); + Location temp = locations->GetTemp(temp_count - extra_temp - 2u); codegen->Move(temp, value); GenerateVarHandleGetAndOp( invoke, codegen, temp, type, field_addr, GetAndUpdateOp::kAdd, byte_swap); diff --git a/test/859-checker-var-handles-intrinsics/info.txt b/test/859-checker-var-handles-intrinsics/info.txt index c6115aee39..5b8f9eb3c5 100644 --- a/test/859-checker-var-handles-intrinsics/info.txt +++ b/test/859-checker-var-handles-intrinsics/info.txt @@ -1 +1,3 @@ -Test that we successfully intrinsify VarHandle's getAndUpdate intrinsics. +Test that we successfully intrinsify VarHandle's getAndUpdate intrinsics +when the return value is ignored by the VarHandle call specifying a void +return type. |