X86: Implement VarHandle.getAndSet, getAndSetAcquire, getAndSetRelease

This commit implements VarHandle getAndSet variants. This implied adding
xchgb and xchgw instructions and tests for them.

Also, all the varType checks were moved to GenerateVarHandleCommonChecks.

Test: ART_HEAP_POISONING=true art/test.py --host -r -t 712-varhandle-invocation --32
Test: ART_HEAP_POISONING=false art/test.py --host -r -t 712-varhandle-invocation --32
Test: ART_USE_READ_BARRIER=true art/test.py --host -r -t 712-varhandle-invocation --32
Test: ART_USE_READ_BARRIER=false art/test.py --host -r -t 712-varhandle-invocation --32
Test: m test-art-host-gtest
Bug: 65872996

Change-Id: I675f47e1dbb51a5ece42f20ad8ce552cfef63ffe
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 3466a07..f520519 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -576,8 +576,11 @@
     DCHECK(instruction_->GetLocations()->Intrinsified());
     Intrinsics intrinsic = instruction_->AsInvoke()->GetIntrinsic();
     static constexpr auto kVarHandleCAS = mirror::VarHandle::AccessModeTemplate::kCompareAndSet;
+    static constexpr auto kVarHandleGetAndSet =
+        mirror::VarHandle::AccessModeTemplate::kGetAndUpdate;
     DCHECK(intrinsic == Intrinsics::kUnsafeCASObject ||
-           mirror::VarHandle::GetAccessModeTemplateByIntrinsic(intrinsic) == kVarHandleCAS);
+           mirror::VarHandle::GetAccessModeTemplateByIntrinsic(intrinsic) == kVarHandleCAS ||
+           mirror::VarHandle::GetAccessModeTemplateByIntrinsic(intrinsic) == kVarHandleGetAndSet);
 
     __ Bind(GetEntryLabel());
     if (unpoison_ref_before_marking_) {
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 661680d..cbd79a1 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -3311,6 +3311,23 @@
                              /* object_can_be_null= */ false);
 }
 
+static void GenerateVarTypePrimitiveTypeCheck(Register varhandle_object,
+                                              Register temp,
+                                              DataType::Type type,
+                                              SlowPathCode* slow_path,
+                                              X86Assembler* assembler) {
+  const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
+  const uint32_t primitive_type_offset = mirror::Class::PrimitiveTypeOffset().Uint32Value();
+  const uint32_t primitive_type = static_cast<uint32_t>(DataTypeToPrimitive(type));
+
+  // We do not need a read barrier when loading a reference only for loading a constant field
+  // through the reference.
+  __ movl(temp, Address(varhandle_object, var_type_offset));
+  __ MaybeUnpoisonHeapReference(temp);
+  __ cmpw(Address(temp, primitive_type_offset), Immediate(primitive_type));
+  __ j(kNotEqual, slow_path->GetEntryLabel());
+}
+
 static void GenerateVarHandleCommonChecks(HInvoke *invoke,
                                           Register temp,
                                           SlowPathCode* slow_path,
@@ -3339,23 +3356,65 @@
       // Unimplemented
       UNREACHABLE();
   }
-}
 
-static void GenerateVarTypePrimitiveTypeCheck(Register varhandle_object,
-                                              Register temp,
-                                              DataType::Type type,
-                                              SlowPathCode* slow_path,
-                                              X86Assembler* assembler) {
-  const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
-  const uint32_t primitive_type_offset = mirror::Class::PrimitiveTypeOffset().Uint32Value();
-  const uint32_t primitive_type = static_cast<uint32_t>(DataTypeToPrimitive(type));
+  // Check the return type and varType parameters.
+  mirror::VarHandle::AccessModeTemplate access_mode_template =
+      mirror::VarHandle::GetAccessModeTemplate(access_mode);
+  DataType::Type type = invoke->GetType();
 
-  // We do not need a read barrier when loading a reference only for loading a constant field
-  // through the reference.
-  __ movl(temp, Address(varhandle_object, var_type_offset));
-  __ MaybeUnpoisonHeapReference(temp);
-  __ cmpw(Address(temp, primitive_type_offset), Immediate(primitive_type));
-  __ j(kNotEqual, slow_path->GetEntryLabel());
+  switch (access_mode_template) {
+    case mirror::VarHandle::AccessModeTemplate::kGet:
+      // Check the varType.primitiveType against the type we're trying to retrieve. Reference types
+      // are also checked later by a HCheckCast node as an additional check.
+      GenerateVarTypePrimitiveTypeCheck(vh_object, temp, type, slow_path, assembler);
+      break;
+    case mirror::VarHandle::AccessModeTemplate::kSet:
+    case mirror::VarHandle::AccessModeTemplate::kGetAndUpdate: {
+      uint32_t value_index = invoke->GetNumberOfArguments() - 1;
+      DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
+
+      // Check the varType.primitiveType against the type of the value we're trying to set.
+      GenerateVarTypePrimitiveTypeCheck(vh_object, temp, value_type, slow_path, assembler);
+      if (value_type == DataType::Type::kReference) {
+        const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
+
+        // If the value type is a reference, check it against the varType.
+        GenerateSubTypeObjectCheck(locations->InAt(value_index).AsRegister<Register>(),
+                                   temp,
+                                   Address(vh_object, var_type_offset),
+                                   slow_path,
+                                   assembler);
+      }
+      break;
+    }
+    case mirror::VarHandle::AccessModeTemplate::kCompareAndSet:
+    case mirror::VarHandle::AccessModeTemplate::kCompareAndExchange: {
+      uint32_t new_value_index = invoke->GetNumberOfArguments() - 1;
+      uint32_t expected_value_index = invoke->GetNumberOfArguments() - 2;
+      DataType::Type value_type = GetDataTypeFromShorty(invoke, new_value_index);
+      DCHECK_EQ(value_type, GetDataTypeFromShorty(invoke, expected_value_index));
+
+      // Check the varType.primitiveType against the type of the expected value.
+      GenerateVarTypePrimitiveTypeCheck(vh_object, temp, value_type, slow_path, assembler);
+      if (value_type == DataType::Type::kReference) {
+        const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
+
+        // If the value type is a reference, check both the expected and the new value against
+        // the varType.
+        GenerateSubTypeObjectCheck(locations->InAt(new_value_index).AsRegister<Register>(),
+                                   temp,
+                                   Address(vh_object, var_type_offset),
+                                   slow_path,
+                                   assembler);
+        GenerateSubTypeObjectCheck(locations->InAt(expected_value_index).AsRegister<Register>(),
+                                   temp,
+                                   Address(vh_object, var_type_offset),
+                                   slow_path,
+                                   assembler);
+      }
+      break;
+    }
+  }
 }
 
 // This method loads the field's address referred by a field VarHandle (base + offset).
@@ -3444,7 +3503,6 @@
 
   X86Assembler* assembler = codegen->GetAssembler();
   LocationSummary* locations = invoke->GetLocations();
-  Register varhandle_object = locations->InAt(0).AsRegister<Register>();
   DataType::Type type = invoke->GetType();
   DCHECK_NE(type, DataType::Type::kVoid);
   Register temp = locations->GetTemp(0).AsRegister<Register>();
@@ -3453,10 +3511,6 @@
 
   GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
 
-  // Check the varType.primitiveType against the type we're trying to retrieve. Reference types
-  // are also checked later by a HCheckCast node as an additional check.
-  GenerateVarTypePrimitiveTypeCheck(varhandle_object, temp, type, slow_path, assembler);
-
   Location out = locations->Out();
   // Use 'out' as a temporary register if it's a core register
   Register offset =
@@ -3604,9 +3658,7 @@
   LocationSummary* locations = invoke->GetLocations();
   // The value we want to set is the last argument
   uint32_t value_index = invoke->GetNumberOfArguments() - 1;
-  Location value = locations->InAt(value_index);
   DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
-  Register varhandle_object = locations->InAt(0).AsRegister<Register>();
   Register temp = locations->GetTemp(0).AsRegister<Register>();
   Register temp2 = locations->GetTemp(1).AsRegister<Register>();
   SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
@@ -3614,25 +3666,12 @@
 
   GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
 
-  // Check the varType.primitiveType against the type of the value we're trying to set.
-  GenerateVarTypePrimitiveTypeCheck(varhandle_object, temp, value_type, slow_path, assembler);
-
-  if (value_type == DataType::Type::kReference) {
-    Register value_reg = value.AsRegister<Register>();
-    const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
-
-    // If the value type is a reference, check it against the varType.
-    GenerateSubTypeObjectCheck(value_reg,
-                               temp,
-                               Address(varhandle_object, var_type_offset),
-                               slow_path,
-                               assembler);
-
-    // For static reference fields, we need another temporary for the declaring class. But since
-    // for instance fields the object is in a separate register, it is safe to use the first
-    // temporary register for GenerateVarHandleFieldReference.
-    size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
-    temp = expected_coordinates_count == 1u ? temp : locations->GetTemp(2).AsRegister<Register>();
+  // For static reference fields, we need another temporary for the declaring class. But since
+  // for instance fields the object is in a separate register, it is safe to use the first
+  // temporary register for GenerateVarHandleFieldReference.
+  size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
+  if (value_type == DataType::Type::kReference && expected_coordinates_count == 0) {
+    temp = locations->GetTemp(2).AsRegister<Register>();
   }
 
   Register offset = temp2;
@@ -3707,6 +3746,177 @@
   GenerateVarHandleSet(invoke, codegen_);
 }
 
+static void CreateVarHandleGetAndSetLocations(HInvoke* invoke) {
+  // The only read barrier implementation supporting the
+  // VarHandleGet intrinsic is the Baker-style read barriers.
+  if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+    return;
+  }
+
+  if (!IsValidFieldVarHandleExpected(invoke)) {
+    return;
+  }
+
+  uint32_t number_of_arguments = invoke->GetNumberOfArguments();
+  uint32_t value_index = number_of_arguments - 1;
+  DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
+
+  if (DataType::Is64BitType(value_type)) {
+    // We avoid the case of an Int64/Float64 value because we would need to place it in a register
+    // pair. If the slow path is taken, the ParallelMove might fail to move the pair according to
+    // the X86DexCallingConvention in case of an overlap (e.g., move the 64 bit value from
+    // <EAX, EBX> to <EBX, ECX>).
+    return;
+  }
+
+  ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
+  LocationSummary* locations = new (allocator) LocationSummary(
+      invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
+  locations->AddTemp(Location::RequiresRegister());
+  locations->AddTemp(Location::RequiresRegister());
+  // We use this temporary for the card, so we need a byte register
+  locations->AddTemp(Location::RegisterLocation(EBX));
+  locations->SetInAt(0, Location::RequiresRegister());
+  if (GetExpectedVarHandleCoordinatesCount(invoke) == 1u) {
+    // For instance fields, this is the source object
+    locations->SetInAt(1, Location::RequiresRegister());
+  } else {
+    // For static fields, we need another temp because one will be busy with the declaring class.
+    locations->AddTemp(Location::RequiresRegister());
+  }
+  if (value_type == DataType::Type::kFloat32) {
+    locations->AddTemp(Location::RegisterLocation(EAX));
+    locations->SetInAt(value_index, Location::FpuRegisterOrConstant(invoke->InputAt(value_index)));
+    locations->SetOut(Location::RequiresFpuRegister());
+  } else {
+    locations->SetInAt(value_index, Location::RegisterLocation(EAX));
+    locations->SetOut(Location::RegisterLocation(EAX));
+  }
+}
+
+static void GenerateVarHandleGetAndSet(HInvoke* invoke, CodeGeneratorX86* codegen) {
+  // The only read barrier implementation supporting the
+  // VarHandleGet intrinsic is the Baker-style read barriers.
+  DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
+
+  X86Assembler* assembler = codegen->GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
+  // The value we want to set is the last argument
+  uint32_t value_index = invoke->GetNumberOfArguments() - 1;
+  Location value = locations->InAt(value_index);
+  DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
+  Register temp = locations->GetTemp(1).AsRegister<Register>();
+  Register temp2 = locations->GetTemp(2).AsRegister<Register>();
+  SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
+  codegen->AddSlowPath(slow_path);
+
+  GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
+
+  Register offset = locations->GetTemp(0).AsRegister<Register>();
+  // Get the field referred by the VarHandle. The returned register contains the object reference
+  // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
+  // declaring class will be placed in 'temp' register.
+  Register reference = GenerateVarHandleFieldReference(invoke, codegen, temp, offset);
+  Address field_addr(reference, offset, TIMES_1, 0);
+
+  if (invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndSetRelease) {
+    codegen->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+  }
+
+  size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
+  // For static fields, we need another temporary for the declaring class. But since for instance
+  // fields the object is in a separate register, it is safe to use the first temporary register.
+  temp = expected_coordinates_count == 1u ? temp : locations->GetTemp(3).AsRegister<Register>();
+  // No need for a lock prefix. `xchg` has an implicit lock when it is used with an address.
+  switch (value_type) {
+    case DataType::Type::kBool:
+      __ xchgb(value.AsRegister<ByteRegister>(), field_addr);
+      __ movzxb(locations->Out().AsRegister<Register>(),
+                locations->Out().AsRegister<ByteRegister>());
+      break;
+    case DataType::Type::kInt8:
+      __ xchgb(value.AsRegister<ByteRegister>(), field_addr);
+      __ movsxb(locations->Out().AsRegister<Register>(),
+                locations->Out().AsRegister<ByteRegister>());
+      break;
+    case DataType::Type::kUint16:
+      __ xchgw(value.AsRegister<Register>(), field_addr);
+      __ movzxw(locations->Out().AsRegister<Register>(), locations->Out().AsRegister<Register>());
+      break;
+    case DataType::Type::kInt16:
+      __ xchgw(value.AsRegister<Register>(), field_addr);
+      __ movsxw(locations->Out().AsRegister<Register>(), locations->Out().AsRegister<Register>());
+      break;
+    case DataType::Type::kInt32:
+      __ xchgl(value.AsRegister<Register>(), field_addr);
+      break;
+    case DataType::Type::kFloat32:
+      codegen->Move32(Location::RegisterLocation(EAX), value);
+      __ xchgl(EAX, field_addr);
+      __ movd(locations->Out().AsFpuRegister<XmmRegister>(), EAX);
+      break;
+    case DataType::Type::kReference: {
+      if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+        // Need to make sure the reference stored in the field is a to-space
+        // one before attempting the CAS or the CAS could fail incorrectly.
+        codegen->GenerateReferenceLoadWithBakerReadBarrier(
+            invoke,
+            // Unused, used only as a "temporary" within the read barrier.
+            Location::RegisterLocation(temp),
+            reference,
+            field_addr,
+            /* needs_null_check= */ false,
+            /* always_update_field= */ true,
+            &temp2);
+      }
+      codegen->MarkGCCard(
+          temp, temp2, reference, value.AsRegister<Register>(), /* value_can_be_null= */ false);
+      if (kPoisonHeapReferences) {
+        __ movl(temp, value.AsRegister<Register>());
+        __ PoisonHeapReference(temp);
+        __ xchgl(temp, field_addr);
+        __ UnpoisonHeapReference(temp);
+        __ movl(locations->Out().AsRegister<Register>(), temp);
+      } else {
+        __ xchgl(locations->Out().AsRegister<Register>(), field_addr);
+      }
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
+
+  if (invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndSetAcquire) {
+    codegen->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+  }
+
+  __ Bind(slow_path->GetExitLabel());
+}
+
+void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndSet(HInvoke* invoke) {
+  CreateVarHandleGetAndSetLocations(invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndSet(HInvoke* invoke) {
+  GenerateVarHandleGetAndSet(invoke, codegen_);
+}
+
+void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndSetAcquire(HInvoke* invoke) {
+  CreateVarHandleGetAndSetLocations(invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndSetAcquire(HInvoke* invoke) {
+  GenerateVarHandleGetAndSet(invoke, codegen_);
+}
+
+void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndSetRelease(HInvoke* invoke) {
+  CreateVarHandleGetAndSetLocations(invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndSetRelease(HInvoke* invoke) {
+  GenerateVarHandleGetAndSet(invoke, codegen_);
+}
+
 static void CreateVarHandleCompareAndSetLocations(HInvoke* invoke) {
   // The only read barrier implementation supporting the
   // VarHandleGet intrinsic is the Baker-style read barriers.
@@ -3778,7 +3988,6 @@
   DCHECK_EQ(type, GetDataTypeFromShorty(invoke, new_value_index));
   Location expected_value = locations->InAt(expected_value_index);
   Location new_value = locations->InAt(new_value_index);
-  Register vh_object = locations->InAt(0).AsRegister<Register>();
   Register offset = locations->GetTemp(0).AsRegister<Register>();
   Register temp = locations->GetTemp(1).AsRegister<Register>();
   Register temp2 = locations->GetTemp(2).AsRegister<Register>();
@@ -3786,24 +3995,6 @@
   codegen->AddSlowPath(slow_path);
 
   GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
-  // Check the varType.primitiveType against the type of the expected value.
-  GenerateVarTypePrimitiveTypeCheck(vh_object, temp, type, slow_path, assembler);
-  if (type == DataType::Type::kReference) {
-    const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
-
-    // If the value type is a reference, check it against the varType.
-    GenerateSubTypeObjectCheck(new_value.AsRegister<Register>(),
-                               temp,
-                               Address(vh_object, var_type_offset),
-                               slow_path,
-                               assembler);
-    // Check the expected value type
-    GenerateSubTypeObjectCheck(expected_value.AsRegister<Register>(),
-                               temp,
-                               Address(vh_object, var_type_offset),
-                               slow_path,
-                               assembler);
-  }
 
   // Get the field referred by the VarHandle. The returned register contains the object reference
   // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
@@ -3932,15 +4123,12 @@
   DataType::Type type = GetDataTypeFromShorty(invoke, value_index);
   DCHECK_EQ(type, invoke->GetType());
   Location value_loc = locations->InAt(value_index);
-  Register vh_object = locations->InAt(0).AsRegister<Register>();
   Register temp = locations->GetTemp(0).AsRegister<Register>();
   SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
   codegen->AddSlowPath(slow_path);
 
   GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
 
-  GenerateVarTypePrimitiveTypeCheck(vh_object, temp, type, slow_path, assembler);
-
   Register offset = locations->GetTemp(1).AsRegister<Register>();
   // Get the field referred by the VarHandle. The returned register contains the object reference
   // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
@@ -4102,15 +4290,12 @@
   uint32_t value_index = invoke->GetNumberOfArguments() - 1;
   DataType::Type type = GetDataTypeFromShorty(invoke, value_index);
   DCHECK_EQ(type, invoke->GetType());
-  Register vh_object = locations->InAt(0).AsRegister<Register>();
   Register temp = locations->GetTemp(0).AsRegister<Register>();
   SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
   codegen->AddSlowPath(slow_path);
 
   GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
 
-  GenerateVarTypePrimitiveTypeCheck(vh_object, temp, type, slow_path, assembler);
-
   Register offset = locations->GetTemp(1).AsRegister<Register>();
   size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
   // For static field, we need another temporary because the first one contains the declaring class
@@ -4284,9 +4469,6 @@
 UNIMPLEMENTED_INTRINSIC(X86, VarHandleCompareAndExchange)
 UNIMPLEMENTED_INTRINSIC(X86, VarHandleCompareAndExchangeAcquire)
 UNIMPLEMENTED_INTRINSIC(X86, VarHandleCompareAndExchangeRelease)
-UNIMPLEMENTED_INTRINSIC(X86, VarHandleGetAndSet)
-UNIMPLEMENTED_INTRINSIC(X86, VarHandleGetAndSetAcquire)
-UNIMPLEMENTED_INTRINSIC(X86, VarHandleGetAndSetRelease)
 
 UNREACHABLE_INTRINSICS(X86)
 
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index da53138..adc7bcc 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -2887,6 +2887,27 @@
 }
 
 
+void X86Assembler::xchgb(Register reg, const Address& address) {
+  // For testing purpose
+  xchgb(static_cast<ByteRegister>(reg), address);
+}
+
+
+void X86Assembler::xchgb(ByteRegister reg, const Address& address) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x86);
+  EmitOperand(reg, address);
+}
+
+
+void X86Assembler::xchgw(Register reg, const Address& address) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOperandSizeOverride();
+  EmitUint8(0x87);
+  EmitOperand(reg, address);
+}
+
+
 void X86Assembler::xchgl(Register dst, Register src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x87);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 3dfeecb..a9050e6 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -695,6 +695,10 @@
   void fptan();
   void fprem();
 
+  void xchgb(ByteRegister reg, const Address& address);
+  void xchgb(Register reg, const Address& address);
+  void xchgw(Register reg, const Address& address);
+
   void xchgl(Register dst, Register src);
   void xchgl(Register reg, const Address& address);
 
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index ee0f8a1..32d138f 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -331,6 +331,21 @@
   DriverStr(expected, "LoadLongConstant");
 }
 
+TEST_F(AssemblerX86Test, Xchgb) {
+  DriverStr(RepeatwA(&x86::X86Assembler::xchgb,
+                     "xchgb {mem}, %{reg}"), "xchgb");
+}
+
+TEST_F(AssemblerX86Test, Xchgw) {
+  DriverStr(RepeatrA(&x86::X86Assembler::xchgw,
+                     "xchgw {mem}, %{reg}"), "xchgw");
+}
+
+TEST_F(AssemblerX86Test, Xchgl) {
+  DriverStr(RepeatRA(&x86::X86Assembler::xchgl,
+                     "xchgl {mem}, %{reg}"), "xchgl");
+}
+
 TEST_F(AssemblerX86Test, LockCmpxchgb) {
   DriverStr(RepeatAw(&x86::X86Assembler::LockCmpxchgb,
                      "lock cmpxchgb %{reg}, {mem}"), "lock_cmpxchgb");