Просмотр исходного кода

Add builtins for compound assignment operators. (#5335)

Provide builtins for compound assignments instead of defining them in
the prelude as a use of a binary operator and an assignment. This allows
us to lower compound assignment directly to LLVM operations instead of
producing a function call. In the short term this also allows us to
define a type-generic compound assignment in the prelude.
Richard Smith 1 год назад
Родитель
Сommit
b5ae988a08
39 измененных файлов с 1372 добавлено и 311 удалено
  1. 20 53
      core/prelude/types/int.carbon
  2. 20 53
      core/prelude/types/uint.carbon
  3. 16 1
      toolchain/check/eval.cpp
  4. 8 8
      toolchain/check/handle_function.cpp
  5. 11 11
      toolchain/check/testdata/array/canonicalize_index.carbon
  6. 47 0
      toolchain/check/testdata/builtins/int/and_assign.carbon
  7. 47 0
      toolchain/check/testdata/builtins/int/left_shift_assign.carbon
  8. 47 0
      toolchain/check/testdata/builtins/int/or_assign.carbon
  9. 47 0
      toolchain/check/testdata/builtins/int/right_shift_assign.carbon
  10. 47 0
      toolchain/check/testdata/builtins/int/sadd_assign.carbon
  11. 47 0
      toolchain/check/testdata/builtins/int/sdiv_assign.carbon
  12. 47 0
      toolchain/check/testdata/builtins/int/smod_assign.carbon
  13. 47 0
      toolchain/check/testdata/builtins/int/smul_assign.carbon
  14. 47 0
      toolchain/check/testdata/builtins/int/ssub_assign.carbon
  15. 47 0
      toolchain/check/testdata/builtins/int/uadd_assign.carbon
  16. 47 0
      toolchain/check/testdata/builtins/int/udiv_assign.carbon
  17. 47 0
      toolchain/check/testdata/builtins/int/umod_assign.carbon
  18. 47 0
      toolchain/check/testdata/builtins/int/umul_assign.carbon
  19. 47 0
      toolchain/check/testdata/builtins/int/usub_assign.carbon
  20. 47 0
      toolchain/check/testdata/builtins/int/xor_assign.carbon
  21. 1 1
      toolchain/check/testdata/class/import.carbon
  22. 10 10
      toolchain/check/testdata/function/generic/param_in_type.carbon
  23. 20 20
      toolchain/check/testdata/if_expr/constant_condition.carbon
  24. 1 1
      toolchain/check/testdata/if_expr/fail_not_in_function.carbon
  25. 4 4
      toolchain/check/testdata/impl/impl_assoc_const.carbon
  26. 2 2
      toolchain/check/testdata/index/fail_negative_indexing.carbon
  27. 2 2
      toolchain/check/testdata/interop/cpp/function_decl.carbon
  28. 3 3
      toolchain/check/testdata/pointer/address_of_deref.carbon
  29. 10 10
      toolchain/check/testdata/pointer/basic.carbon
  30. 18 18
      toolchain/check/testdata/pointer/import.carbon
  31. 3 3
      toolchain/check/testdata/struct/import.carbon
  32. 2 2
      toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon
  33. 3 3
      toolchain/check/testdata/tuple/import.carbon
  34. 5 0
      toolchain/lower/function_context.cpp
  35. 3 0
      toolchain/lower/function_context.h
  36. 151 105
      toolchain/lower/handle_call.cpp
  37. 240 0
      toolchain/lower/testdata/builtins/int.carbon
  38. 97 1
      toolchain/sem_ir/builtin_function_kind.cpp
  39. 17 0
      toolchain/sem_ir/builtin_function_kind.def

+ 20 - 53
core/prelude/types/int.carbon

@@ -106,77 +106,44 @@ impl forall [N:! IntLiteral()] Int(N) as RightShift {
 
 // Compound assignments.
 
-// TODO: Once we can lower specific functions, make these generic.
-// Each function has a commented out generic signature.
-
-// impl forall [N:! IntLiteral()] Int(N) as AddAssign {
-impl i32 as AddAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self + other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as AddAssign {
+  fn Op[addr self: Self*](other: Self) = "int.sadd_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as BitAndAssign {
-impl i32 as BitAndAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self & other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as BitAndAssign {
+  fn Op[addr self: Self*](other: Self) = "int.and_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as BitOrAssign {
-impl i32 as BitOrAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self | other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as BitOrAssign {
+  fn Op[addr self: Self*](other: Self) = "int.or_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as BitXorAssign {
-impl i32 as BitXorAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self ^ other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as BitXorAssign {
+  fn Op[addr self: Self*](other: Self) = "int.xor_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as DivAssign {
-impl i32 as DivAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self / other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as DivAssign {
+  fn Op[addr self: Self*](other: Self) = "int.sdiv_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as LeftShiftAssign {
-impl i32 as LeftShiftAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self << other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as LeftShiftAssign {
+  fn Op[addr self: Self*](other: Self) = "int.left_shift_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as ModAssign {
-impl i32 as ModAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self % other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as ModAssign {
+  fn Op[addr self: Self*](other: Self) = "int.smod_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as MulAssign {
-impl i32 as MulAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self * other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as MulAssign {
+  fn Op[addr self: Self*](other: Self) = "int.smul_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as RightShiftAssign {
-impl i32 as RightShiftAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self >> other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as RightShiftAssign {
+  fn Op[addr self: Self*](other: Self) = "int.right_shift_assign";
 }
 
-// impl forall [N:! IntLiteral()] Int(N) as SubAssign {
-impl i32 as SubAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self - other;
-  }
+impl forall [N:! IntLiteral()] Int(N) as SubAssign {
+  fn Op[addr self: Self*](other: Self) = "int.ssub_assign";
 }
 
 // Increment and decrement.

+ 20 - 53
core/prelude/types/uint.carbon

@@ -117,77 +117,44 @@ impl forall [N:! IntLiteral()] UInt(N) as RightShift {
 
 // Compound assignments.
 
-// TODO: Once we can lower specific functions, make these generic.
-// Each function has a commented out generic signature.
-
-// impl forall [N:! IntLiteral()] UInt(N) as AddAssign {
-impl u32 as AddAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self + other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as AddAssign {
+  fn Op[addr self: Self*](other: Self) = "int.uadd_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as BitAndAssign {
-impl u32 as BitAndAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self & other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as BitAndAssign {
+  fn Op[addr self: Self*](other: Self) = "int.and_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as BitOrAssign {
-impl u32 as BitOrAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self | other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as BitOrAssign {
+  fn Op[addr self: Self*](other: Self) = "int.or_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as BitXorAssign {
-impl u32 as BitXorAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self ^ other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as BitXorAssign {
+  fn Op[addr self: Self*](other: Self) = "int.xor_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as DivAssign {
-impl u32 as DivAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self / other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as DivAssign {
+  fn Op[addr self: Self*](other: Self) = "int.udiv_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as LeftShiftAssign {
-impl u32 as LeftShiftAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self << other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as LeftShiftAssign {
+  fn Op[addr self: Self*](other: Self) = "int.left_shift_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as ModAssign {
-impl u32 as ModAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self % other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as ModAssign {
+  fn Op[addr self: Self*](other: Self) = "int.umod_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as MulAssign {
-impl u32 as MulAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self * other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as MulAssign {
+  fn Op[addr self: Self*](other: Self) = "int.umul_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as RightShiftAssign {
-impl u32 as RightShiftAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self >> other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as RightShiftAssign {
+  fn Op[addr self: Self*](other: Self) = "int.right_shift_assign";
 }
 
-// impl forall [N:! IntLiteral()] UInt(N) as SubAssign {
-impl u32 as SubAssign {
-  fn Op[addr self: Self*](other: Self) {
-    *self = *self - other;
-  }
+impl forall [N:! IntLiteral()] UInt(N) as SubAssign {
+  fn Op[addr self: Self*](other: Self) = "int.usub_assign";
 }
 
 // Increment and decrement.

+ 16 - 1
toolchain/check/eval.cpp

@@ -1394,7 +1394,22 @@ static auto MakeConstantForBuiltinCall(EvalContext& eval_context,
 
     case SemIR::BuiltinFunctionKind::PrintChar:
     case SemIR::BuiltinFunctionKind::PrintInt:
-    case SemIR::BuiltinFunctionKind::ReadChar: {
+    case SemIR::BuiltinFunctionKind::ReadChar:
+    case SemIR::BuiltinFunctionKind::IntSAddAssign:
+    case SemIR::BuiltinFunctionKind::IntSSubAssign:
+    case SemIR::BuiltinFunctionKind::IntSMulAssign:
+    case SemIR::BuiltinFunctionKind::IntSDivAssign:
+    case SemIR::BuiltinFunctionKind::IntSModAssign:
+    case SemIR::BuiltinFunctionKind::IntUAddAssign:
+    case SemIR::BuiltinFunctionKind::IntUSubAssign:
+    case SemIR::BuiltinFunctionKind::IntUMulAssign:
+    case SemIR::BuiltinFunctionKind::IntUDivAssign:
+    case SemIR::BuiltinFunctionKind::IntUModAssign:
+    case SemIR::BuiltinFunctionKind::IntAndAssign:
+    case SemIR::BuiltinFunctionKind::IntOrAssign:
+    case SemIR::BuiltinFunctionKind::IntXorAssign:
+    case SemIR::BuiltinFunctionKind::IntLeftShiftAssign:
+    case SemIR::BuiltinFunctionKind::IntRightShiftAssign: {
       // These are runtime-only builtins.
       // TODO: Consider tracking this on the `BuiltinFunctionKind`.
       return SemIR::ConstantId::NotConstant;

+ 8 - 8
toolchain/check/handle_function.cpp

@@ -696,16 +696,16 @@ static auto IsValidBuiltinDeclaration(Context& context,
                                       const SemIR::Function& function,
                                       SemIR::BuiltinFunctionKind builtin_kind)
     -> bool {
+  // Find the list of call parameters other than the implicit return slot.
+  auto call_params = context.inst_blocks().Get(function.call_params_id);
+  if (function.return_slot_pattern_id.has_value()) {
+    call_params = call_params.drop_back();
+  }
+
   // Form the list of parameter types for the declaration.
   llvm::SmallVector<SemIR::TypeId> param_type_ids;
-  auto implicit_param_patterns =
-      context.inst_blocks().GetOrEmpty(function.implicit_param_patterns_id);
-  auto param_patterns =
-      context.inst_blocks().GetOrEmpty(function.param_patterns_id);
-  param_type_ids.reserve(implicit_param_patterns.size() +
-                         param_patterns.size());
-  for (auto param_id : llvm::concat<const SemIR::InstId>(
-           implicit_param_patterns, param_patterns)) {
+  param_type_ids.reserve(call_params.size());
+  for (auto param_id : call_params) {
     // TODO: We also need to track whether the parameter is declared with
     // `var`.
     param_type_ids.push_back(context.insts().Get(param_id).type_id());

+ 11 - 11
toolchain/check/testdata/array/canonicalize_index.carbon

@@ -56,7 +56,7 @@ let c: array(i32, ConvertToU32(3))* = &a;
 // CHECK:STDOUT:   %bound_method.c6f: <bound method> = bound_method %int_3.822, %Convert.specific_fn.8a8 [concrete]
 // CHECK:STDOUT:   %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
 // CHECK:STDOUT:   %array_type: type = array_type %int_3.1ba, %i32 [concrete]
-// CHECK:STDOUT:   %ptr: type = ptr_type %array_type [concrete]
+// CHECK:STDOUT:   %ptr.f01: type = ptr_type %array_type [concrete]
 // CHECK:STDOUT:   %tuple.type: type = tuple_type (Core.IntLiteral, Core.IntLiteral, Core.IntLiteral) [concrete]
 // CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete]
 // CHECK:STDOUT:   %Convert.bound.b30: <bound method> = bound_method %int_3.1ba, %Convert.956 [concrete]
@@ -175,20 +175,20 @@ let c: array(i32, ConvertToU32(3))* = &a;
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %a: ref %array_type = bind_name a, %a.var
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %b.patt: %ptr = binding_pattern b
+// CHECK:STDOUT:     %b.patt: %ptr.f01 = binding_pattern b
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc15: type = splice_block %ptr.loc15 [concrete = constants.%ptr] {
+// CHECK:STDOUT:   %.loc15: type = splice_block %ptr.loc15 [concrete = constants.%ptr.f01] {
 // CHECK:STDOUT:     %int_32.loc15: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:     %i32.loc15: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:     %int_3.loc15: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
 // CHECK:STDOUT:     %array_type.loc15: type = array_type %int_3.loc15, %i32.loc15 [concrete = constants.%array_type]
-// CHECK:STDOUT:     %ptr.loc15: type = ptr_type %array_type.loc15 [concrete = constants.%ptr]
+// CHECK:STDOUT:     %ptr.loc15: type = ptr_type %array_type.loc15 [concrete = constants.%ptr.f01]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %b: %ptr = bind_name b, @__global_init.%addr.loc15
+// CHECK:STDOUT:   %b: %ptr.f01 = bind_name b, @__global_init.%addr.loc15
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %c.patt: %ptr = binding_pattern c
+// CHECK:STDOUT:     %c.patt: %ptr.f01 = binding_pattern c
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc16_35: type = splice_block %ptr.loc16 [concrete = constants.%ptr] {
+// CHECK:STDOUT:   %.loc16_35: type = splice_block %ptr.loc16 [concrete = constants.%ptr.f01] {
 // CHECK:STDOUT:     %int_32.loc16: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:     %i32.loc16: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:     %ConvertToU32.ref: %ConvertToU32.type = name_ref ConvertToU32, %ConvertToU32.decl [concrete = constants.%ConvertToU32]
@@ -211,9 +211,9 @@ let c: array(i32, ConvertToU32(3))* = &a;
 // CHECK:STDOUT:     %.loc16_33.3: Core.IntLiteral = value_of_initializer %int.convert_checked.loc16_33.2 [concrete = constants.%int_3.1ba]
 // CHECK:STDOUT:     %.loc16_33.4: Core.IntLiteral = converted %int.convert_checked.loc16_33.1, %.loc16_33.3 [concrete = constants.%int_3.1ba]
 // CHECK:STDOUT:     %array_type.loc16: type = array_type %.loc16_33.4, %i32.loc16 [concrete = constants.%array_type]
-// CHECK:STDOUT:     %ptr.loc16: type = ptr_type %array_type.loc16 [concrete = constants.%ptr]
+// CHECK:STDOUT:     %ptr.loc16: type = ptr_type %array_type.loc16 [concrete = constants.%ptr.f01]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %c: %ptr = bind_name c, @__global_init.%addr.loc16
+// CHECK:STDOUT:   %c: %ptr.f01 = bind_name c, @__global_init.%addr.loc16
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Add.1(%a.param_patt: %i32, %b.param_patt: %i32) -> %i32 = "int.sadd";
@@ -257,9 +257,9 @@ let c: array(i32, ConvertToU32(3))* = &a;
 // CHECK:STDOUT:   %.loc14_1: init %array_type = converted %.loc14_40.1, %.loc14_40.11 [concrete = constants.%array]
 // CHECK:STDOUT:   assign file.%a.var, %.loc14_1
 // CHECK:STDOUT:   %a.ref.loc15: ref %array_type = name_ref a, file.%a
-// CHECK:STDOUT:   %addr.loc15: %ptr = addr_of %a.ref.loc15
+// CHECK:STDOUT:   %addr.loc15: %ptr.f01 = addr_of %a.ref.loc15
 // CHECK:STDOUT:   %a.ref.loc16: ref %array_type = name_ref a, file.%a
-// CHECK:STDOUT:   %addr.loc16: %ptr = addr_of %a.ref.loc16
+// CHECK:STDOUT:   %addr.loc16: %ptr.f01 = addr_of %a.ref.loc16
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 47 - 0
toolchain/check/testdata/builtins/int/and_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/and_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/and_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.and_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.and_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.and_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.and_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.and_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.and_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.and_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/left_shift_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/left_shift_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/left_shift_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.left_shift_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+fn MixedTypes(a: i32*, b: i64) = "int.left_shift_assign";
+
+fn CallMixed(a: i32*, b: i64) {
+  MixedTypes(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.left_shift_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.left_shift_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.left_shift_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.left_shift_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.left_shift_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.left_shift_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/or_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/or_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/or_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.or_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.or_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.or_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.or_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.or_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.or_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.or_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.or_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.or_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.or_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/right_shift_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/right_shift_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/right_shift_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.right_shift_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+fn MixedTypes(a: i32*, b: i64) = "int.right_shift_assign";
+
+fn CallMixed(a: i32*, b: i64) {
+  MixedTypes(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.right_shift_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.right_shift_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.right_shift_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.right_shift_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.right_shift_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.right_shift_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/sadd_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/sadd_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/sadd_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.sadd_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.sadd_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.sadd_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.sadd_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.sadd_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.sadd_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.sadd_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/sdiv_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/sdiv_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/sdiv_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.sdiv_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sdiv_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.sdiv_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.sdiv_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sdiv_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.sdiv_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.sdiv_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sdiv_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.sdiv_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.sdiv_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/smod_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/smod_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/smod_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.smod_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smod_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.smod_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.smod_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smod_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.smod_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.smod_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smod_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.smod_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.smod_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/smul_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/smul_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/smul_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.smul_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smul_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.smul_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.smul_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smul_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.smul_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.smul_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smul_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.smul_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.smul_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/ssub_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/ssub_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/ssub_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.ssub_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.ssub_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.ssub_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.ssub_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.ssub_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.ssub_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.ssub_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.ssub_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.ssub_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.ssub_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/uadd_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/uadd_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/uadd_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.uadd_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.uadd_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.uadd_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.uadd_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.uadd_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.uadd_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.uadd_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/udiv_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/udiv_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/udiv_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.udiv_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.udiv_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.udiv_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.udiv_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.udiv_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.udiv_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.udiv_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.udiv_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.udiv_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.udiv_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/umod_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/umod_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/umod_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.umod_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umod_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.umod_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.umod_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umod_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.umod_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.umod_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umod_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.umod_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.umod_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/umul_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/umul_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/umul_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.umul_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umul_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.umul_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.umul_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umul_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.umul_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.umul_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umul_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.umul_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.umul_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/usub_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/usub_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/usub_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.usub_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.usub_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.usub_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.usub_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.usub_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.usub_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.usub_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.usub_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.usub_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.usub_assign";

+ 47 - 0
toolchain/check/testdata/builtins/int/xor_assign.carbon

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// EXTRA-ARGS: --no-dump-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/xor_assign.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/xor_assign.carbon
+
+// --- call.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Builtin(a: i32*, b: i32) = "int.xor_assign";
+
+fn Call(a: i32*, b: i32) {
+  Builtin(a, b);
+}
+
+// --- fail_unsized.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.xor_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.xor_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn Builtin(a: Core.IntLiteral()*, b: Core.IntLiteral()) = "int.xor_assign";
+
+// --- fail_bad_decl.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.xor_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn NotPtr(a: i32, b: i32) = "int.xor_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn NotPtr(a: i32, b: i32) = "int.xor_assign";
+
+// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.xor_assign" [InvalidBuiltinSignature]
+// CHECK:STDERR: fn MixedTypes(a: i32*, b: i64) = "int.xor_assign";
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MixedTypes(a: i32*, b: i64) = "int.xor_assign";

+ 1 - 1
toolchain/check/testdata/class/import.carbon

@@ -368,5 +368,5 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F[%self.param_patt: %ForwardDeclared.7b34f2.1]() [from "a.carbon"];
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G[addr <unexpected>.inst1161: %ptr.6cf]() [from "a.carbon"];
+// CHECK:STDOUT: fn @G[addr <unexpected>.inst1395: %ptr.6cf]() [from "a.carbon"];
 // CHECK:STDOUT:

+ 10 - 10
toolchain/check/testdata/function/generic/param_in_type.carbon

@@ -30,7 +30,7 @@ fn F(N:! i32, a: array(i32, N)*);
 // CHECK:STDOUT:   %bound_method: <bound method> = bound_method %N.51e, %Convert.specific_fn [symbolic]
 // CHECK:STDOUT:   %int.convert_checked: init Core.IntLiteral = call %bound_method(%N.51e) [symbolic]
 // CHECK:STDOUT:   %array_type: type = array_type %int.convert_checked, %i32 [symbolic]
-// CHECK:STDOUT:   %ptr: type = ptr_type %array_type [symbolic]
+// CHECK:STDOUT:   %ptr.0ad: type = ptr_type %array_type [symbolic]
 // CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
 // CHECK:STDOUT: }
@@ -52,16 +52,16 @@ fn F(N:! i32, a: array(i32, N)*);
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
 // CHECK:STDOUT:     %N.patt.loc11_6.1: %i32 = symbolic_binding_pattern N, 0 [symbolic = %N.patt.loc11_6.2 (constants.%N.patt.8e2)]
-// CHECK:STDOUT:     %a.patt: @F.%ptr.loc11_31.2 (%ptr) = binding_pattern a
-// CHECK:STDOUT:     %a.param_patt: @F.%ptr.loc11_31.2 (%ptr) = value_param_pattern %a.patt, call_param0
+// CHECK:STDOUT:     %a.patt: @F.%ptr.loc11_31.2 (%ptr.0ad) = binding_pattern a
+// CHECK:STDOUT:     %a.param_patt: @F.%ptr.loc11_31.2 (%ptr.0ad) = value_param_pattern %a.patt, call_param0
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %.loc11_10: type = splice_block %i32.loc11_10 [concrete = constants.%i32] {
 // CHECK:STDOUT:       %int_32.loc11_10: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:       %i32.loc11_10: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %N.loc11_6.1: %i32 = bind_symbolic_name N, 0 [symbolic = %N.loc11_6.2 (constants.%N.51e)]
-// CHECK:STDOUT:     %a.param: @F.%ptr.loc11_31.2 (%ptr) = value_param call_param0
-// CHECK:STDOUT:     %.loc11_31: type = splice_block %ptr.loc11_31.1 [symbolic = %ptr.loc11_31.2 (constants.%ptr)] {
+// CHECK:STDOUT:     %a.param: @F.%ptr.loc11_31.2 (%ptr.0ad) = value_param call_param0
+// CHECK:STDOUT:     %.loc11_31: type = splice_block %ptr.loc11_31.1 [symbolic = %ptr.loc11_31.2 (constants.%ptr.0ad)] {
 // CHECK:STDOUT:       %int_32.loc11_24: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:       %i32.loc11_24: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:       %N.ref: %i32 = name_ref N, %N.loc11_6.1 [symbolic = %N.loc11_6.2 (constants.%N.51e)]
@@ -73,9 +73,9 @@ fn F(N:! i32, a: array(i32, N)*);
 // CHECK:STDOUT:       %.loc11_29.1: Core.IntLiteral = value_of_initializer %int.convert_checked.loc11_29.1 [symbolic = %int.convert_checked.loc11_29.2 (constants.%int.convert_checked)]
 // CHECK:STDOUT:       %.loc11_29.2: Core.IntLiteral = converted %N.ref, %.loc11_29.1 [symbolic = %int.convert_checked.loc11_29.2 (constants.%int.convert_checked)]
 // CHECK:STDOUT:       %array_type.loc11_30.1: type = array_type %.loc11_29.2, %i32.loc11_24 [symbolic = %array_type.loc11_30.2 (constants.%array_type)]
-// CHECK:STDOUT:       %ptr.loc11_31.1: type = ptr_type %array_type.loc11_30.1 [symbolic = %ptr.loc11_31.2 (constants.%ptr)]
+// CHECK:STDOUT:       %ptr.loc11_31.1: type = ptr_type %array_type.loc11_30.1 [symbolic = %ptr.loc11_31.2 (constants.%ptr.0ad)]
 // CHECK:STDOUT:     }
-// CHECK:STDOUT:     %a: @F.%ptr.loc11_31.2 (%ptr) = bind_name a, %a.param
+// CHECK:STDOUT:     %a: @F.%ptr.loc11_31.2 (%ptr.0ad) = bind_name a, %a.param
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -86,9 +86,9 @@ fn F(N:! i32, a: array(i32, N)*);
 // CHECK:STDOUT:   %bound_method.loc11_29.3: <bound method> = bound_method %N.loc11_6.2, constants.%Convert.specific_fn [symbolic = %bound_method.loc11_29.3 (constants.%bound_method)]
 // CHECK:STDOUT:   %int.convert_checked.loc11_29.2: init Core.IntLiteral = call %bound_method.loc11_29.3(%N.loc11_6.2) [symbolic = %int.convert_checked.loc11_29.2 (constants.%int.convert_checked)]
 // CHECK:STDOUT:   %array_type.loc11_30.2: type = array_type %int.convert_checked.loc11_29.2, constants.%i32 [symbolic = %array_type.loc11_30.2 (constants.%array_type)]
-// CHECK:STDOUT:   %ptr.loc11_31.2: type = ptr_type %array_type.loc11_30.2 [symbolic = %ptr.loc11_31.2 (constants.%ptr)]
+// CHECK:STDOUT:   %ptr.loc11_31.2: type = ptr_type %array_type.loc11_30.2 [symbolic = %ptr.loc11_31.2 (constants.%ptr.0ad)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%N.patt.loc11_6.1: %i32, %a.param_patt: @F.%ptr.loc11_31.2 (%ptr));
+// CHECK:STDOUT:   fn(%N.patt.loc11_6.1: %i32, %a.param_patt: @F.%ptr.loc11_31.2 (%ptr.0ad));
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%N.51e) {
@@ -98,6 +98,6 @@ fn F(N:! i32, a: array(i32, N)*);
 // CHECK:STDOUT:   %bound_method.loc11_29.3 => constants.%bound_method
 // CHECK:STDOUT:   %int.convert_checked.loc11_29.2 => constants.%int.convert_checked
 // CHECK:STDOUT:   %array_type.loc11_30.2 => constants.%array_type
-// CHECK:STDOUT:   %ptr.loc11_31.2 => constants.%ptr
+// CHECK:STDOUT:   %ptr.loc11_31.2 => constants.%ptr.0ad
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 20 - 20
toolchain/check/testdata/if_expr/constant_condition.carbon

@@ -65,7 +65,7 @@ fn PartiallyConstant(t: type) -> i32 {
 // CHECK:STDOUT:   %false: bool = bool_literal false [concrete]
 // CHECK:STDOUT:   %Constant.type: type = fn_type @Constant [concrete]
 // CHECK:STDOUT:   %Constant: %Constant.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr: type = ptr_type %i32 [concrete]
+// CHECK:STDOUT:   %ptr.235: type = ptr_type %i32 [concrete]
 // CHECK:STDOUT:   %PartiallyConstant.type: type = fn_type @PartiallyConstant [concrete]
 // CHECK:STDOUT:   %PartiallyConstant: %PartiallyConstant.type = struct_value () [concrete]
 // CHECK:STDOUT: }
@@ -253,7 +253,7 @@ fn PartiallyConstant(t: type) -> i32 {
 // CHECK:STDOUT: !if.expr.else.loc23:
 // CHECK:STDOUT:   %int_32.loc23_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:   %i32.loc23_32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:   %ptr.loc23: type = ptr_type %i32.loc23_32 [concrete = constants.%ptr]
+// CHECK:STDOUT:   %ptr.loc23: type = ptr_type %i32.loc23_32 [concrete = constants.%ptr.235]
 // CHECK:STDOUT:   br !if.expr.result.loc23(%ptr.loc23)
 // CHECK:STDOUT:
 // CHECK:STDOUT: !if.expr.result.loc23:
@@ -263,12 +263,12 @@ fn PartiallyConstant(t: type) -> i32 {
 // CHECK:STDOUT: !.loc23_7:
 // CHECK:STDOUT:   %v: ref %i32 = bind_name v, %v.var
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %w.patt: %ptr = binding_pattern w
-// CHECK:STDOUT:     %.loc24_3: %ptr = var_pattern %w.patt
+// CHECK:STDOUT:     %w.patt: %ptr.235 = binding_pattern w
+// CHECK:STDOUT:     %.loc24_3: %ptr.235 = var_pattern %w.patt
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %w.var: ref %ptr = var w
+// CHECK:STDOUT:   %w.var: ref %ptr.235 = var w
 // CHECK:STDOUT:   %v.ref: ref %i32 = name_ref v, %v
-// CHECK:STDOUT:   %addr: %ptr = addr_of %v.ref
+// CHECK:STDOUT:   %addr: %ptr.235 = addr_of %v.ref
 // CHECK:STDOUT:   assign %w.var, %addr
 // CHECK:STDOUT:   br !.loc24_13
 // CHECK:STDOUT:
@@ -284,17 +284,17 @@ fn PartiallyConstant(t: type) -> i32 {
 // CHECK:STDOUT: !if.expr.else.loc24:
 // CHECK:STDOUT:   %int_32.loc24_33: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:   %i32.loc24_33: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:   %ptr.loc24: type = ptr_type %i32.loc24_33 [concrete = constants.%ptr]
+// CHECK:STDOUT:   %ptr.loc24: type = ptr_type %i32.loc24_33 [concrete = constants.%ptr.235]
 // CHECK:STDOUT:   br !if.expr.result.loc24(%ptr.loc24)
 // CHECK:STDOUT:
 // CHECK:STDOUT: !if.expr.result.loc24:
-// CHECK:STDOUT:   %.loc24_10: type = block_arg !if.expr.result.loc24 [concrete = constants.%ptr]
+// CHECK:STDOUT:   %.loc24_10: type = block_arg !if.expr.result.loc24 [concrete = constants.%ptr.235]
 // CHECK:STDOUT:   br !.loc24_7
 // CHECK:STDOUT:
 // CHECK:STDOUT: !.loc24_7:
-// CHECK:STDOUT:   %w: ref %ptr = bind_name w, %w.var
-// CHECK:STDOUT:   %w.ref: ref %ptr = name_ref w, %w
-// CHECK:STDOUT:   %.loc25_11: %ptr = bind_value %w.ref
+// CHECK:STDOUT:   %w: ref %ptr.235 = bind_name w, %w.var
+// CHECK:STDOUT:   %w.ref: ref %ptr.235 = name_ref w, %w
+// CHECK:STDOUT:   %.loc25_11: %ptr.235 = bind_value %w.ref
 // CHECK:STDOUT:   %.loc25_10.1: ref %i32 = deref %.loc25_11
 // CHECK:STDOUT:   %.loc25_10.2: %i32 = bind_value %.loc25_10.1
 // CHECK:STDOUT:   return %.loc25_10.2
@@ -337,12 +337,12 @@ fn PartiallyConstant(t: type) -> i32 {
 // CHECK:STDOUT: !.loc29_7:
 // CHECK:STDOUT:   %v: ref %i32 = bind_name v, %v.var
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %w.patt: %ptr = binding_pattern w
-// CHECK:STDOUT:     %.loc30_3: %ptr = var_pattern %w.patt
+// CHECK:STDOUT:     %w.patt: %ptr.235 = binding_pattern w
+// CHECK:STDOUT:     %.loc30_3: %ptr.235 = var_pattern %w.patt
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %w.var: ref %ptr = var w
+// CHECK:STDOUT:   %w.var: ref %ptr.235 = var w
 // CHECK:STDOUT:   %v.ref: ref %i32 = name_ref v, %v
-// CHECK:STDOUT:   %addr: %ptr = addr_of %v.ref
+// CHECK:STDOUT:   %addr: %ptr.235 = addr_of %v.ref
 // CHECK:STDOUT:   assign %w.var, %addr
 // CHECK:STDOUT:   br !.loc30_13
 // CHECK:STDOUT:
@@ -357,17 +357,17 @@ fn PartiallyConstant(t: type) -> i32 {
 // CHECK:STDOUT: !if.expr.else.loc30:
 // CHECK:STDOUT:   %int_32.loc30: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:   %i32.loc30: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:   %ptr: type = ptr_type %i32.loc30 [concrete = constants.%ptr]
+// CHECK:STDOUT:   %ptr: type = ptr_type %i32.loc30 [concrete = constants.%ptr.235]
 // CHECK:STDOUT:   br !if.expr.result.loc30(%ptr)
 // CHECK:STDOUT:
 // CHECK:STDOUT: !if.expr.result.loc30:
-// CHECK:STDOUT:   %.loc30_10: type = block_arg !if.expr.result.loc30 [concrete = constants.%ptr]
+// CHECK:STDOUT:   %.loc30_10: type = block_arg !if.expr.result.loc30 [concrete = constants.%ptr.235]
 // CHECK:STDOUT:   br !.loc30_7
 // CHECK:STDOUT:
 // CHECK:STDOUT: !.loc30_7:
-// CHECK:STDOUT:   %w: ref %ptr = bind_name w, %w.var
-// CHECK:STDOUT:   %w.ref: ref %ptr = name_ref w, %w
-// CHECK:STDOUT:   %.loc31_11: %ptr = bind_value %w.ref
+// CHECK:STDOUT:   %w: ref %ptr.235 = bind_name w, %w.var
+// CHECK:STDOUT:   %w.ref: ref %ptr.235 = name_ref w, %w
+// CHECK:STDOUT:   %.loc31_11: %ptr.235 = bind_value %w.ref
 // CHECK:STDOUT:   %.loc31_10.1: ref %i32 = deref %.loc31_11
 // CHECK:STDOUT:   %.loc31_10.2: %i32 = bind_value %.loc31_10.1
 // CHECK:STDOUT:   return %.loc31_10.2

+ 1 - 1
toolchain/check/testdata/if_expr/fail_not_in_function.carbon

@@ -94,7 +94,7 @@ class C {
 // CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %x: %i32 = bind_name x, <unexpected>.inst1083.loc27_14
+// CHECK:STDOUT:   %x: %i32 = bind_name x, <unexpected>.inst1317.loc27_14
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %y.patt: %i32 = binding_pattern y
 // CHECK:STDOUT:     %.loc37: %i32 = var_pattern %y.patt

+ 4 - 4
toolchain/check/testdata/impl/impl_assoc_const.carbon

@@ -71,21 +71,21 @@ impl () as I where .X = {.a = true, .b = (1, 2)} and .X = {.a = false, .b = (3,
 // CHECK:STDOUT:   %struct: %struct_type.a.b.fe2 = struct_value (%true, %tuple) [concrete]
 // CHECK:STDOUT:   %false: bool = bool_literal false [concrete]
 // CHECK:STDOUT:   %int_3: Core.IntLiteral = int_value 3 [concrete]
-// CHECK:STDOUT:   %Op.type.111: type = fn_type @Op.13 [concrete]
+// CHECK:STDOUT:   %Op.type.111: type = fn_type @Op.23 [concrete]
 // CHECK:STDOUT:   %Sub.impl_witness_table.d7b = impl_witness_table (imports.%Core.import_ref.6c4), @impl.bf8 [concrete]
 // CHECK:STDOUT:   %Sub.impl_witness.37d: <witness> = impl_witness %Sub.impl_witness_table.d7b [concrete]
 // CHECK:STDOUT:   %Sub.facet: %Sub.type = facet_value Core.IntLiteral, (%Sub.impl_witness.37d) [concrete]
 // CHECK:STDOUT:   %.07b: type = fn_type_with_self_type %Op.type.111, %Sub.facet [concrete]
-// CHECK:STDOUT:   %Op.type.2b9: type = fn_type @Op.14 [concrete]
+// CHECK:STDOUT:   %Op.type.2b9: type = fn_type @Op.24 [concrete]
 // CHECK:STDOUT:   %Op.667: %Op.type.2b9 = struct_value () [concrete]
 // CHECK:STDOUT:   %Op.bound.645: <bound method> = bound_method %int_3, %Op.667 [concrete]
 // CHECK:STDOUT:   %int_4: Core.IntLiteral = int_value 4 [concrete]
-// CHECK:STDOUT:   %Op.type.784: type = fn_type @Op.15 [concrete]
+// CHECK:STDOUT:   %Op.type.784: type = fn_type @Op.25 [concrete]
 // CHECK:STDOUT:   %Div.impl_witness_table.381 = impl_witness_table (imports.%Core.import_ref.dfd), @impl.f0e [concrete]
 // CHECK:STDOUT:   %Div.impl_witness.ec4: <witness> = impl_witness %Div.impl_witness_table.381 [concrete]
 // CHECK:STDOUT:   %Div.facet: %Div.type = facet_value Core.IntLiteral, (%Div.impl_witness.ec4) [concrete]
 // CHECK:STDOUT:   %.d13: type = fn_type_with_self_type %Op.type.784, %Div.facet [concrete]
-// CHECK:STDOUT:   %Op.type.c38: type = fn_type @Op.16 [concrete]
+// CHECK:STDOUT:   %Op.type.c38: type = fn_type @Op.26 [concrete]
 // CHECK:STDOUT:   %Op.553: %Op.type.c38 = struct_value () [concrete]
 // CHECK:STDOUT:   %Op.bound.2e7: <bound method> = bound_method %int_4, %Op.553 [concrete]
 // CHECK:STDOUT:   %I_where.type: type = facet_type <@I where %impl.elem0 = %struct> [concrete]

+ 2 - 2
toolchain/check/testdata/index/fail_negative_indexing.carbon

@@ -42,12 +42,12 @@ var d: i32 = c[-10];
 // CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete]
 // CHECK:STDOUT:   %array: %array_type = tuple_value (%int_42.c68, %int_42.c68) [concrete]
 // CHECK:STDOUT:   %int_10: Core.IntLiteral = int_value 10 [concrete]
-// CHECK:STDOUT:   %Op.type.e42: type = fn_type @Op.13 [concrete]
+// CHECK:STDOUT:   %Op.type.e42: type = fn_type @Op.23 [concrete]
 // CHECK:STDOUT:   %Negate.impl_witness_table.e09 = impl_witness_table (imports.%Core.import_ref.c15), @impl.8cb [concrete]
 // CHECK:STDOUT:   %Negate.impl_witness.561: <witness> = impl_witness %Negate.impl_witness_table.e09 [concrete]
 // CHECK:STDOUT:   %Negate.facet: %Negate.type = facet_value Core.IntLiteral, (%Negate.impl_witness.561) [concrete]
 // CHECK:STDOUT:   %.63a: type = fn_type_with_self_type %Op.type.e42, %Negate.facet [concrete]
-// CHECK:STDOUT:   %Op.type.1be: type = fn_type @Op.14 [concrete]
+// CHECK:STDOUT:   %Op.type.1be: type = fn_type @Op.24 [concrete]
 // CHECK:STDOUT:   %Op.bba: %Op.type.1be = struct_value () [concrete]
 // CHECK:STDOUT:   %Op.bound: <bound method> = bound_method %int_10, %Op.bba [concrete]
 // CHECK:STDOUT:   %int_-10.06d: Core.IntLiteral = int_value -10 [concrete]

+ 2 - 2
toolchain/check/testdata/interop/cpp/function_decl.carbon

@@ -895,7 +895,7 @@ fn F() {
 // CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.956, @Convert.2(%int_32) [concrete]
 // CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_1.5b8, %Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %int_1.5d2: %i32 = int_value 1 [concrete]
-// CHECK:STDOUT:   %ptr: type = ptr_type %i32 [concrete]
+// CHECK:STDOUT:   %ptr.235: type = ptr_type %i32 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -947,7 +947,7 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
 // CHECK:STDOUT:   %a.ref: ref %i32 = name_ref a, %a
-// CHECK:STDOUT:   %addr: %ptr = addr_of %a.ref
+// CHECK:STDOUT:   %addr: %ptr.235 = addr_of %a.ref
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 3 - 3
toolchain/check/testdata/pointer/address_of_deref.carbon

@@ -33,7 +33,7 @@ fn F() -> i32 {
 // CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.956, @Convert.2(%int_32) [concrete]
 // CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_0.5c6, %Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %int_0.6a9: %i32 = int_value 0 [concrete]
-// CHECK:STDOUT:   %ptr: type = ptr_type %i32 [concrete]
+// CHECK:STDOUT:   %ptr.235: type = ptr_type %i32 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -83,9 +83,9 @@ fn F() -> i32 {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %n: ref %i32 = bind_name n, %n.var
 // CHECK:STDOUT:   %n.ref: ref %i32 = name_ref n, %n
-// CHECK:STDOUT:   %addr.loc13_13: %ptr = addr_of %n.ref
+// CHECK:STDOUT:   %addr.loc13_13: %ptr.235 = addr_of %n.ref
 // CHECK:STDOUT:   %.loc13_12: ref %i32 = deref %addr.loc13_13
-// CHECK:STDOUT:   %addr.loc13_11: %ptr = addr_of %.loc13_12
+// CHECK:STDOUT:   %addr.loc13_11: %ptr.235 = addr_of %.loc13_12
 // CHECK:STDOUT:   %.loc13_10.1: ref %i32 = deref %addr.loc13_11
 // CHECK:STDOUT:   %.loc13_10.2: %i32 = bind_value %.loc13_10.1
 // CHECK:STDOUT:   return %.loc13_10.2

+ 10 - 10
toolchain/check/testdata/pointer/basic.carbon

@@ -35,7 +35,7 @@ fn F() -> i32 {
 // CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.956, @Convert.2(%int_32) [concrete]
 // CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_0.5c6, %Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %int_0.6a9: %i32 = int_value 0 [concrete]
-// CHECK:STDOUT:   %ptr: type = ptr_type %i32 [concrete]
+// CHECK:STDOUT:   %ptr.235: type = ptr_type %i32 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -85,21 +85,21 @@ fn F() -> i32 {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %n: ref %i32 = bind_name n, %n.var
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %p.patt: %ptr = binding_pattern p
-// CHECK:STDOUT:     %.loc13_3: %ptr = var_pattern %p.patt
+// CHECK:STDOUT:     %p.patt: %ptr.235 = binding_pattern p
+// CHECK:STDOUT:     %.loc13_3: %ptr.235 = var_pattern %p.patt
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %p.var: ref %ptr = var p
+// CHECK:STDOUT:   %p.var: ref %ptr.235 = var p
 // CHECK:STDOUT:   %n.ref: ref %i32 = name_ref n, %n
-// CHECK:STDOUT:   %addr: %ptr = addr_of %n.ref
+// CHECK:STDOUT:   %addr: %ptr.235 = addr_of %n.ref
 // CHECK:STDOUT:   assign %p.var, %addr
-// CHECK:STDOUT:   %.loc13_13: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:   %.loc13_13: type = splice_block %ptr [concrete = constants.%ptr.235] {
 // CHECK:STDOUT:     %int_32.loc13: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:     %i32.loc13: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:     %ptr: type = ptr_type %i32.loc13 [concrete = constants.%ptr]
+// CHECK:STDOUT:     %ptr: type = ptr_type %i32.loc13 [concrete = constants.%ptr.235]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %p: ref %ptr = bind_name p, %p.var
-// CHECK:STDOUT:   %p.ref: ref %ptr = name_ref p, %p
-// CHECK:STDOUT:   %.loc15_11: %ptr = bind_value %p.ref
+// CHECK:STDOUT:   %p: ref %ptr.235 = bind_name p, %p.var
+// CHECK:STDOUT:   %p.ref: ref %ptr.235 = name_ref p, %p
+// CHECK:STDOUT:   %.loc15_11: %ptr.235 = bind_value %p.ref
 // CHECK:STDOUT:   %.loc15_10.1: ref %i32 = deref %.loc15_11
 // CHECK:STDOUT:   %.loc15_10.2: %i32 = bind_value %.loc15_10.1
 // CHECK:STDOUT:   return %.loc15_10.2

+ 18 - 18
toolchain/check/testdata/pointer/import.carbon

@@ -39,7 +39,7 @@ var a: i32* = a_ref;
 // CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.956, @Convert.2(%int_32) [concrete]
 // CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_0.5c6, %Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %int_0.6a9: %i32 = int_value 0 [concrete]
-// CHECK:STDOUT:   %ptr: type = ptr_type %i32 [concrete]
+// CHECK:STDOUT:   %ptr.235: type = ptr_type %i32 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -69,16 +69,16 @@ var a: i32* = a_ref;
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %a_orig: ref %i32 = bind_name a_orig, %a_orig.var
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %a_ref.patt: %ptr = binding_pattern a_ref
-// CHECK:STDOUT:     %.loc5_1: %ptr = var_pattern %a_ref.patt
+// CHECK:STDOUT:     %a_ref.patt: %ptr.235 = binding_pattern a_ref
+// CHECK:STDOUT:     %.loc5_1: %ptr.235 = var_pattern %a_ref.patt
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %a_ref.var: ref %ptr = var a_ref
-// CHECK:STDOUT:   %.loc5_15: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:   %a_ref.var: ref %ptr.235 = var a_ref
+// CHECK:STDOUT:   %.loc5_15: type = splice_block %ptr [concrete = constants.%ptr.235] {
 // CHECK:STDOUT:     %int_32.loc5: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:     %i32.loc5: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:     %ptr: type = ptr_type %i32.loc5 [concrete = constants.%ptr]
+// CHECK:STDOUT:     %ptr: type = ptr_type %i32.loc5 [concrete = constants.%ptr.235]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %a_ref: ref %ptr = bind_name a_ref, %a_ref.var
+// CHECK:STDOUT:   %a_ref: ref %ptr.235 = bind_name a_ref, %a_ref.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
@@ -92,7 +92,7 @@ var a: i32* = a_ref;
 // CHECK:STDOUT:   %.loc4: init %i32 = converted %int_0, %int.convert_checked [concrete = constants.%int_0.6a9]
 // CHECK:STDOUT:   assign file.%a_orig.var, %.loc4
 // CHECK:STDOUT:   %a_orig.ref: ref %i32 = name_ref a_orig, file.%a_orig
-// CHECK:STDOUT:   %addr: %ptr = addr_of %a_orig.ref
+// CHECK:STDOUT:   %addr: %ptr.235 = addr_of %a_orig.ref
 // CHECK:STDOUT:   assign file.%a_ref.var, %addr
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -102,12 +102,12 @@ var a: i32* = a_ref;
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
-// CHECK:STDOUT:   %ptr: type = ptr_type %i32 [concrete]
+// CHECK:STDOUT:   %ptr.9e1: type = ptr_type %i32 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Implicit.a_orig = import_ref Implicit//default, a_orig, unloaded
-// CHECK:STDOUT:   %Implicit.a_ref: ref %ptr = import_ref Implicit//default, a_ref, loaded
+// CHECK:STDOUT:   %Implicit.a_ref: ref %ptr.9e1 = import_ref Implicit//default, a_ref, loaded
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
 // CHECK:STDOUT:     .Int = %Core.Int
 // CHECK:STDOUT:     import Core//prelude
@@ -126,22 +126,22 @@ var a: i32* = a_ref;
 // CHECK:STDOUT:   %default.import = import <none>
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %a.patt: %ptr = binding_pattern a
-// CHECK:STDOUT:     %.loc4_1: %ptr = var_pattern %a.patt
+// CHECK:STDOUT:     %a.patt: %ptr.9e1 = binding_pattern a
+// CHECK:STDOUT:     %.loc4_1: %ptr.9e1 = var_pattern %a.patt
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %a.var: ref %ptr = var a
-// CHECK:STDOUT:   %.loc4_11: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:   %a.var: ref %ptr.9e1 = var a
+// CHECK:STDOUT:   %.loc4_11: type = splice_block %ptr [concrete = constants.%ptr.9e1] {
 // CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:     %ptr: type = ptr_type %i32 [concrete = constants.%ptr]
+// CHECK:STDOUT:     %ptr: type = ptr_type %i32 [concrete = constants.%ptr.9e1]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %a: ref %ptr = bind_name a, %a.var
+// CHECK:STDOUT:   %a: ref %ptr.9e1 = bind_name a, %a.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %a_ref.ref: ref %ptr = name_ref a_ref, imports.%Implicit.a_ref
-// CHECK:STDOUT:   %.loc4: %ptr = bind_value %a_ref.ref
+// CHECK:STDOUT:   %a_ref.ref: ref %ptr.9e1 = name_ref a_ref, imports.%Implicit.a_ref
+// CHECK:STDOUT:   %.loc4: %ptr.9e1 = bind_value %a_ref.ref
 // CHECK:STDOUT:   assign file.%a.var, %.loc4
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 3 - 3
toolchain/check/testdata/struct/import.carbon

@@ -331,7 +331,7 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b.5ca = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1160 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1394 [no loc], unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -508,7 +508,7 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1160 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1394 [no loc], unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -626,7 +626,7 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b.5ca = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1160 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1394 [no loc], unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {

+ 2 - 2
toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon

@@ -43,12 +43,12 @@ var b: i32 = a.(-10);
 // CHECK:STDOUT:   %int_6.e56: %i32 = int_value 6 [concrete]
 // CHECK:STDOUT:   %tuple: %tuple.type.d07 = tuple_value (%int_12.1e1, %int_6.e56) [concrete]
 // CHECK:STDOUT:   %int_10: Core.IntLiteral = int_value 10 [concrete]
-// CHECK:STDOUT:   %Op.type.e42: type = fn_type @Op.13 [concrete]
+// CHECK:STDOUT:   %Op.type.e42: type = fn_type @Op.23 [concrete]
 // CHECK:STDOUT:   %Negate.impl_witness_table.e09 = impl_witness_table (imports.%Core.import_ref.c15), @impl.8cb [concrete]
 // CHECK:STDOUT:   %Negate.impl_witness.561: <witness> = impl_witness %Negate.impl_witness_table.e09 [concrete]
 // CHECK:STDOUT:   %Negate.facet: %Negate.type = facet_value Core.IntLiteral, (%Negate.impl_witness.561) [concrete]
 // CHECK:STDOUT:   %.63a: type = fn_type_with_self_type %Op.type.e42, %Negate.facet [concrete]
-// CHECK:STDOUT:   %Op.type.1be: type = fn_type @Op.14 [concrete]
+// CHECK:STDOUT:   %Op.type.1be: type = fn_type @Op.24 [concrete]
 // CHECK:STDOUT:   %Op.bba: %Op.type.1be = struct_value () [concrete]
 // CHECK:STDOUT:   %Op.bound: <bound method> = bound_method %int_10, %Op.bba [concrete]
 // CHECK:STDOUT:   %int_-10: Core.IntLiteral = int_value -10 [concrete]

+ 3 - 3
toolchain/check/testdata/tuple/import.carbon

@@ -361,7 +361,7 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1197 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1431 [no loc], unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -554,7 +554,7 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1197 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1431 [no loc], unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -673,7 +673,7 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1197 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1431 [no loc], unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {

+ 5 - 0
toolchain/lower/function_context.cpp

@@ -7,6 +7,7 @@
 #include "common/vlog.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/sem_ir/file.h"
+#include "toolchain/sem_ir/generic.h"
 
 namespace Carbon::Lower {
 
@@ -171,6 +172,10 @@ auto FunctionContext::FinishInit(SemIR::TypeId type_id, SemIR::InstId dest_id,
   }
 }
 
+auto FunctionContext::GetTypeOfInst(SemIR::InstId inst_id) -> SemIR::TypeId {
+  return SemIR::GetTypeOfInstInSpecific(sem_ir(), specific_id(), inst_id);
+}
+
 auto FunctionContext::CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id,
                                 SemIR::InstId dest_id) -> void {
   switch (auto rep = SemIR::ValueRepr::ForType(sem_ir(), type_id); rep.kind) {

+ 3 - 0
toolchain/lower/function_context.h

@@ -92,6 +92,9 @@ class FunctionContext {
     return file_context_->GetType(type_id);
   }
 
+  // Returns the type of the given instruction in the current specific.
+  auto GetTypeOfInst(SemIR::InstId inst_id) -> SemIR::TypeId;
+
   // Returns a lowered value to use for a value of type `type`.
   auto GetTypeAsValue() -> llvm::Value* {
     return file_context_->GetTypeAsValue();

+ 151 - 105
toolchain/lower/handle_call.cpp

@@ -64,8 +64,7 @@ static auto GetBuiltinFCmpPredicate(SemIR::BuiltinFunctionKind builtin_kind)
 // Returns whether the specified instruction has a signed integer type.
 static auto IsSignedInt(FunctionContext& context, SemIR::InstId int_id)
     -> bool {
-  return context.sem_ir().types().IsSignedInt(
-      context.sem_ir().insts().Get(int_id).type_id());
+  return context.sem_ir().types().IsSignedInt(context.GetTypeOfInst(int_id));
 }
 
 // Creates a zext or sext instruction depending on the signedness of the
@@ -86,13 +85,10 @@ static auto CreateExtOrTrunc(FunctionContext& context, llvm::Value* value,
                    : context.builder().CreateZExtOrTrunc(value, type, name);
 }
 
-// Handles a call to a builtin integer bit shift operator.
-static auto HandleIntShift(FunctionContext& context, SemIR::InstId inst_id,
+// Create a integer bit shift for a call to a builtin bit shift function.
+static auto CreateIntShift(FunctionContext& context,
                            llvm::Instruction::BinaryOps bin_op,
-                           SemIR::InstId lhs_id, SemIR::InstId rhs_id) -> void {
-  llvm::Value* lhs = context.GetValue(lhs_id);
-  llvm::Value* rhs = context.GetValue(rhs_id);
-
+                           llvm::Value* lhs, llvm::Value* rhs) -> llvm::Value* {
   // Weirdly, LLVM requires the operands of bit shift operators to be of the
   // same type. We can always use the width of the LHS, because if the RHS
   // doesn't fit in that then the cast is out of range anyway. Zero-extending is
@@ -102,8 +98,7 @@ static auto HandleIntShift(FunctionContext& context, SemIR::InstId inst_id,
   // negative or greater than or equal to the number of bits in the left-hand
   // type.
   rhs = context.builder().CreateZExtOrTrunc(rhs, lhs->getType(), "rhs");
-
-  context.SetLocal(inst_id, context.builder().CreateBinOp(bin_op, lhs, rhs));
+  return context.builder().CreateBinOp(bin_op, lhs, rhs);
 }
 
 // Handles a call to a builtin integer comparison operator.
@@ -150,6 +145,102 @@ static auto HandleIntComparison(FunctionContext& context, SemIR::InstId inst_id,
           GetBuiltinICmpPredicate(builtin_kind, cmp_signed), lhs, rhs));
 }
 
+// Creates a binary operator for a call to a builtin for either that operation
+// or the corresponding compound assignment.
+static auto CreateBinaryOperatorForBuiltin(
+    FunctionContext& context, SemIR::InstId inst_id,
+    SemIR::BuiltinFunctionKind builtin_kind, llvm::Value* lhs, llvm::Value* rhs)
+    -> llvm::Value* {
+  // TODO: Consider setting this to true in the performance build mode if the
+  // result type is a signed integer type.
+  constexpr bool SignedOverflowIsUB = false;
+
+  switch (builtin_kind) {
+    case SemIR::BuiltinFunctionKind::IntSAdd:
+    case SemIR::BuiltinFunctionKind::IntSAddAssign: {
+      return context.builder().CreateAdd(lhs, rhs, "",
+                                         /*HasNUW=*/false,
+                                         /*HasNSW=*/SignedOverflowIsUB);
+    }
+    case SemIR::BuiltinFunctionKind::IntSSub:
+    case SemIR::BuiltinFunctionKind::IntSSubAssign: {
+      return context.builder().CreateSub(lhs, rhs, "",
+                                         /*HasNUW=*/false,
+                                         /*HasNSW=*/SignedOverflowIsUB);
+    }
+    case SemIR::BuiltinFunctionKind::IntSMul:
+    case SemIR::BuiltinFunctionKind::IntSMulAssign: {
+      return context.builder().CreateMul(lhs, rhs, "",
+                                         /*HasNUW=*/false,
+                                         /*HasNSW=*/SignedOverflowIsUB);
+    }
+    case SemIR::BuiltinFunctionKind::IntSDiv:
+    case SemIR::BuiltinFunctionKind::IntSDivAssign: {
+      return context.builder().CreateSDiv(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntSMod:
+    case SemIR::BuiltinFunctionKind::IntSModAssign: {
+      return context.builder().CreateSRem(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntUAdd:
+    case SemIR::BuiltinFunctionKind::IntUAddAssign: {
+      return context.builder().CreateAdd(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntUSub:
+    case SemIR::BuiltinFunctionKind::IntUSubAssign: {
+      return context.builder().CreateSub(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntUMul:
+    case SemIR::BuiltinFunctionKind::IntUMulAssign: {
+      return context.builder().CreateMul(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntUDiv:
+    case SemIR::BuiltinFunctionKind::IntUDivAssign: {
+      return context.builder().CreateUDiv(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntUMod:
+    case SemIR::BuiltinFunctionKind::IntUModAssign: {
+      return context.builder().CreateURem(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntAnd:
+    case SemIR::BuiltinFunctionKind::IntAndAssign: {
+      return context.builder().CreateAnd(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntOr:
+    case SemIR::BuiltinFunctionKind::IntOrAssign: {
+      return context.builder().CreateOr(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntXor:
+    case SemIR::BuiltinFunctionKind::IntXorAssign: {
+      return context.builder().CreateXor(lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntLeftShift:
+    case SemIR::BuiltinFunctionKind::IntLeftShiftAssign: {
+      return CreateIntShift(context, llvm::Instruction::Shl, lhs, rhs);
+    }
+    case SemIR::BuiltinFunctionKind::IntRightShift:
+    case SemIR::BuiltinFunctionKind::IntRightShiftAssign: {
+      // TODO: Split each of these builtins into separate signed and unsigned
+      // builtins rather than working out here whether we're performing an
+      // arithmetic or logical shift.
+      auto lhs_id = context.sem_ir().inst_blocks().Get(
+          context.sem_ir().insts().GetAs<SemIR::Call>(inst_id).args_id)[0];
+      auto lhs_type_id = context.GetTypeOfInst(lhs_id);
+      if (builtin_kind == SemIR::BuiltinFunctionKind::IntRightShiftAssign) {
+        lhs_type_id = context.sem_ir().GetPointeeType(lhs_type_id);
+      }
+      return CreateIntShift(context,
+                            context.sem_ir().types().IsSignedInt(lhs_type_id)
+                                ? llvm::Instruction::AShr
+                                : llvm::Instruction::LShr,
+                            lhs, rhs);
+    }
+    default: {
+      CARBON_FATAL("Unexpected binary operator {0}", builtin_kind);
+    }
+  }
+}
+
 // Handles a call to a builtin function.
 static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id,
                               SemIR::BuiltinFunctionKind builtin_kind,
@@ -262,100 +353,56 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id,
               llvm::ConstantInt::getSigned(operand->getType(), -1), operand));
       return;
     }
-    case SemIR::BuiltinFunctionKind::IntSAdd: {
-      context.SetLocal(
-          inst_id, context.builder().CreateAdd(context.GetValue(arg_ids[0]),
-                                               context.GetValue(arg_ids[1]), "",
-                                               /*HasNUW=*/false,
-                                               /*HasNSW=*/SignedOverflowIsUB));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntSSub: {
-      context.SetLocal(
-          inst_id, context.builder().CreateSub(context.GetValue(arg_ids[0]),
-                                               context.GetValue(arg_ids[1]), "",
-                                               /*HasNUW=*/false,
-                                               /*HasNSW=*/SignedOverflowIsUB));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntSMul: {
-      context.SetLocal(
-          inst_id, context.builder().CreateMul(context.GetValue(arg_ids[0]),
-                                               context.GetValue(arg_ids[1]), "",
-                                               /*HasNUW=*/false,
-                                               /*HasNSW=*/SignedOverflowIsUB));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntSDiv: {
-      context.SetLocal(
-          inst_id, context.builder().CreateSDiv(context.GetValue(arg_ids[0]),
-                                                context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntSMod: {
-      context.SetLocal(
-          inst_id, context.builder().CreateSRem(context.GetValue(arg_ids[0]),
-                                                context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntUAdd: {
-      context.SetLocal(
-          inst_id, context.builder().CreateAdd(context.GetValue(arg_ids[0]),
-                                               context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntUSub: {
-      context.SetLocal(
-          inst_id, context.builder().CreateSub(context.GetValue(arg_ids[0]),
-                                               context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntUMul: {
-      context.SetLocal(
-          inst_id, context.builder().CreateMul(context.GetValue(arg_ids[0]),
-                                               context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntUDiv: {
-      context.SetLocal(
-          inst_id, context.builder().CreateUDiv(context.GetValue(arg_ids[0]),
-                                                context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntUMod: {
-      context.SetLocal(
-          inst_id, context.builder().CreateURem(context.GetValue(arg_ids[0]),
-                                                context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntAnd: {
-      context.SetLocal(
-          inst_id, context.builder().CreateAnd(context.GetValue(arg_ids[0]),
-                                               context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntOr: {
-      context.SetLocal(
-          inst_id, context.builder().CreateOr(context.GetValue(arg_ids[0]),
-                                              context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntXor: {
-      context.SetLocal(
-          inst_id, context.builder().CreateXor(context.GetValue(arg_ids[0]),
-                                               context.GetValue(arg_ids[1])));
-      return;
-    }
-    case SemIR::BuiltinFunctionKind::IntLeftShift: {
-      HandleIntShift(context, inst_id, llvm::Instruction::Shl, arg_ids[0],
-                     arg_ids[1]);
+    case SemIR::BuiltinFunctionKind::IntSAdd:
+    case SemIR::BuiltinFunctionKind::IntSSub:
+    case SemIR::BuiltinFunctionKind::IntSMul:
+    case SemIR::BuiltinFunctionKind::IntSDiv:
+    case SemIR::BuiltinFunctionKind::IntSMod:
+    case SemIR::BuiltinFunctionKind::IntUAdd:
+    case SemIR::BuiltinFunctionKind::IntUSub:
+    case SemIR::BuiltinFunctionKind::IntUMul:
+    case SemIR::BuiltinFunctionKind::IntUDiv:
+    case SemIR::BuiltinFunctionKind::IntUMod:
+    case SemIR::BuiltinFunctionKind::IntAnd:
+    case SemIR::BuiltinFunctionKind::IntOr:
+    case SemIR::BuiltinFunctionKind::IntXor:
+    case SemIR::BuiltinFunctionKind::IntLeftShift:
+    case SemIR::BuiltinFunctionKind::IntRightShift: {
+      context.SetLocal(inst_id, CreateBinaryOperatorForBuiltin(
+                                    context, inst_id, builtin_kind,
+                                    context.GetValue(arg_ids[0]),
+                                    context.GetValue(arg_ids[1])));
       return;
     }
-    case SemIR::BuiltinFunctionKind::IntRightShift: {
-      HandleIntShift(context, inst_id,
-                     IsSignedInt(context, inst_id) ? llvm::Instruction::AShr
-                                                   : llvm::Instruction::LShr,
-                     arg_ids[0], arg_ids[1]);
+    case SemIR::BuiltinFunctionKind::IntSAddAssign:
+    case SemIR::BuiltinFunctionKind::IntSSubAssign:
+    case SemIR::BuiltinFunctionKind::IntSMulAssign:
+    case SemIR::BuiltinFunctionKind::IntSDivAssign:
+    case SemIR::BuiltinFunctionKind::IntSModAssign:
+    case SemIR::BuiltinFunctionKind::IntUAddAssign:
+    case SemIR::BuiltinFunctionKind::IntUSubAssign:
+    case SemIR::BuiltinFunctionKind::IntUMulAssign:
+    case SemIR::BuiltinFunctionKind::IntUDivAssign:
+    case SemIR::BuiltinFunctionKind::IntUModAssign:
+    case SemIR::BuiltinFunctionKind::IntAndAssign:
+    case SemIR::BuiltinFunctionKind::IntOrAssign:
+    case SemIR::BuiltinFunctionKind::IntXorAssign:
+    case SemIR::BuiltinFunctionKind::IntLeftShiftAssign:
+    case SemIR::BuiltinFunctionKind::IntRightShiftAssign: {
+      auto* lhs_ptr = context.GetValue(arg_ids[0]);
+      auto lhs_type_id = context.GetTypeOfInst(arg_ids[0]);
+      auto pointee_type_id = context.sem_ir().GetPointeeType(lhs_type_id);
+      // TODO: Factor out the code to create loads and stores, and include alias
+      // and alignment information.
+      auto* lhs_value = context.builder().CreateLoad(
+          context.GetType(pointee_type_id), lhs_ptr);
+      auto* result = CreateBinaryOperatorForBuiltin(
+          context, inst_id, builtin_kind, lhs_value,
+          context.GetValue(arg_ids[1]));
+      context.builder().CreateStore(result, lhs_ptr);
+      // TODO: Add a helper to get a "no value representation" value.
+      context.SetLocal(inst_id, llvm::PoisonValue::get(context.GetType(
+                                    context.GetTypeOfInst(inst_id))));
       return;
     }
     case SemIR::BuiltinFunctionKind::IntEq:
@@ -444,8 +491,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
 
   std::vector<llvm::Value*> args;
 
-  auto inst_type_id = SemIR::GetTypeOfInstInSpecific(
-      context.sem_ir(), context.specific_id(), inst_id);
+  auto inst_type_id = context.GetTypeOfInst(inst_id);
 
   if (SemIR::ReturnTypeInfo::ForType(context.sem_ir(), inst_type_id)
           .has_return_slot()) {
@@ -454,7 +500,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
   }
 
   for (auto arg_id : arg_ids) {
-    auto arg_type_id = context.sem_ir().insts().Get(arg_id).type_id();
+    auto arg_type_id = context.GetTypeOfInst(arg_id);
     if (SemIR::ValueRepr::ForType(context.sem_ir(), arg_type_id).kind !=
         SemIR::ValueRepr::None) {
       args.push_back(context.GetValue(arg_id));

+ 240 - 0
toolchain/lower/testdata/builtins/int.carbon

@@ -69,6 +69,58 @@ fn TestGreater(a: i32, b: i32) -> bool { return Greater(a, b); }
 fn GreaterEq(a: i32, b: i32) -> bool = "int.greater_eq";
 fn TestGreaterEq(a: i32, b: i32) -> bool { return GreaterEq(a, b); }
 
+// --- compound_assign.carbon
+
+library "[[@TEST_NAME]]";
+
+fn SAdd(a: i32*, b: i32) = "int.sadd_assign";
+fn TestSAdd(a: i32*, b: i32) { SAdd(a, b); }
+
+fn SSub(a: i32*, b: i32) = "int.ssub_assign";
+fn TestSSub(a: i32*, b: i32) { SSub(a, b); }
+
+fn SMul(a: i32*, b: i32) = "int.smul_assign";
+fn TestSMul(a: i32*, b: i32) { SMul(a, b); }
+
+fn SDiv(a: i32*, b: i32) = "int.sdiv_assign";
+fn TestSDiv(a: i32*, b: i32) { SDiv(a, b); }
+
+fn SMod(a: i32*, b: i32) = "int.smod_assign";
+fn TestSMod(a: i32*, b: i32) { SMod(a, b); }
+
+fn UAdd(a: i32*, b: i32) = "int.uadd_assign";
+fn TestUAdd(a: i32*, b: i32) { UAdd(a, b); }
+
+fn USub(a: i32*, b: i32) = "int.usub_assign";
+fn TestUSub(a: i32*, b: i32) { USub(a, b); }
+
+fn UMul(a: i32*, b: i32) = "int.umul_assign";
+fn TestUMul(a: i32*, b: i32) { UMul(a, b); }
+
+fn UDiv(a: i32*, b: i32) = "int.udiv_assign";
+fn TestUDiv(a: i32*, b: i32) { UDiv(a, b); }
+
+fn UMod(a: i32*, b: i32) = "int.umod_assign";
+fn TestUMod(a: i32*, b: i32) { UMod(a, b); }
+
+fn And(a: i32*, b: i32) = "int.and_assign";
+fn TestAnd(a: i32*, b: i32) { And(a, b); }
+
+fn Or(a: i32*, b: i32) = "int.or_assign";
+fn TestOr(a: i32*, b: i32) { Or(a, b); }
+
+fn Xor(a: i32*, b: i32) = "int.xor_assign";
+fn TestXor(a: i32*, b: i32) { Xor(a, b); }
+
+fn LeftShift(a: i32*, b: i32) = "int.left_shift_assign";
+fn TestLeftShift(a: i32*, b: i32) { LeftShift(a, b); }
+
+fn ArithmeticRightShift(a: i32*, b: u32) = "int.right_shift_assign";
+fn TestArithmeticRightShift(a: i32*, b: u32) { ArithmeticRightShift(a, b); }
+
+fn LogicalRightShift(a: u32*, b: u32) = "int.right_shift_assign";
+fn TestLogicalRightShift(a: u32*, b: u32) { LogicalRightShift(a, b); }
+
 // --- mixed_shift.carbon
 
 library "[[@TEST_NAME]]";
@@ -351,6 +403,194 @@ fn TestUint32ToUint64(a: u32) -> u64 { return Uint32ToUint64(a); }
 // CHECK:STDOUT: !60 = distinct !DISubprogram(name: "TestGreaterEq", linkageName: "_CTestGreaterEq.Main", scope: null, file: !3, line: 59, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !61 = !DILocation(line: 59, column: 51, scope: !60)
 // CHECK:STDOUT: !62 = !DILocation(line: 59, column: 44, scope: !60)
+// CHECK:STDOUT: ; ModuleID = 'compound_assign.carbon'
+// CHECK:STDOUT: source_filename = "compound_assign.carbon"
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestSAdd.Main(ptr %a, i32 %b) !dbg !4 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.sadd_assign = load i32, ptr %a, align 4, !dbg !7
+// CHECK:STDOUT:   %int.sadd_assign1 = add i32 %int.sadd_assign, %b, !dbg !7
+// CHECK:STDOUT:   store i32 %int.sadd_assign1, ptr %a, align 4, !dbg !7
+// CHECK:STDOUT:   ret void, !dbg !8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestSSub.Main(ptr %a, i32 %b) !dbg !9 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.ssub_assign = load i32, ptr %a, align 4, !dbg !10
+// CHECK:STDOUT:   %int.ssub_assign1 = sub i32 %int.ssub_assign, %b, !dbg !10
+// CHECK:STDOUT:   store i32 %int.ssub_assign1, ptr %a, align 4, !dbg !10
+// CHECK:STDOUT:   ret void, !dbg !11
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestSMul.Main(ptr %a, i32 %b) !dbg !12 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.smul_assign = load i32, ptr %a, align 4, !dbg !13
+// CHECK:STDOUT:   %int.smul_assign1 = mul i32 %int.smul_assign, %b, !dbg !13
+// CHECK:STDOUT:   store i32 %int.smul_assign1, ptr %a, align 4, !dbg !13
+// CHECK:STDOUT:   ret void, !dbg !14
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestSDiv.Main(ptr %a, i32 %b) !dbg !15 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.sdiv_assign = load i32, ptr %a, align 4, !dbg !16
+// CHECK:STDOUT:   %int.sdiv_assign1 = sdiv i32 %int.sdiv_assign, %b, !dbg !16
+// CHECK:STDOUT:   store i32 %int.sdiv_assign1, ptr %a, align 4, !dbg !16
+// CHECK:STDOUT:   ret void, !dbg !17
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestSMod.Main(ptr %a, i32 %b) !dbg !18 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.smod_assign = load i32, ptr %a, align 4, !dbg !19
+// CHECK:STDOUT:   %int.smod_assign1 = srem i32 %int.smod_assign, %b, !dbg !19
+// CHECK:STDOUT:   store i32 %int.smod_assign1, ptr %a, align 4, !dbg !19
+// CHECK:STDOUT:   ret void, !dbg !20
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestUAdd.Main(ptr %a, i32 %b) !dbg !21 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.uadd_assign = load i32, ptr %a, align 4, !dbg !22
+// CHECK:STDOUT:   %int.uadd_assign1 = add i32 %int.uadd_assign, %b, !dbg !22
+// CHECK:STDOUT:   store i32 %int.uadd_assign1, ptr %a, align 4, !dbg !22
+// CHECK:STDOUT:   ret void, !dbg !23
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestUSub.Main(ptr %a, i32 %b) !dbg !24 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.usub_assign = load i32, ptr %a, align 4, !dbg !25
+// CHECK:STDOUT:   %int.usub_assign1 = sub i32 %int.usub_assign, %b, !dbg !25
+// CHECK:STDOUT:   store i32 %int.usub_assign1, ptr %a, align 4, !dbg !25
+// CHECK:STDOUT:   ret void, !dbg !26
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestUMul.Main(ptr %a, i32 %b) !dbg !27 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.umul_assign = load i32, ptr %a, align 4, !dbg !28
+// CHECK:STDOUT:   %int.umul_assign1 = mul i32 %int.umul_assign, %b, !dbg !28
+// CHECK:STDOUT:   store i32 %int.umul_assign1, ptr %a, align 4, !dbg !28
+// CHECK:STDOUT:   ret void, !dbg !29
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestUDiv.Main(ptr %a, i32 %b) !dbg !30 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.udiv_assign = load i32, ptr %a, align 4, !dbg !31
+// CHECK:STDOUT:   %int.udiv_assign1 = udiv i32 %int.udiv_assign, %b, !dbg !31
+// CHECK:STDOUT:   store i32 %int.udiv_assign1, ptr %a, align 4, !dbg !31
+// CHECK:STDOUT:   ret void, !dbg !32
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestUMod.Main(ptr %a, i32 %b) !dbg !33 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.umod_assign = load i32, ptr %a, align 4, !dbg !34
+// CHECK:STDOUT:   %int.umod_assign1 = urem i32 %int.umod_assign, %b, !dbg !34
+// CHECK:STDOUT:   store i32 %int.umod_assign1, ptr %a, align 4, !dbg !34
+// CHECK:STDOUT:   ret void, !dbg !35
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestAnd.Main(ptr %a, i32 %b) !dbg !36 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.and_assign = load i32, ptr %a, align 4, !dbg !37
+// CHECK:STDOUT:   %int.and_assign1 = and i32 %int.and_assign, %b, !dbg !37
+// CHECK:STDOUT:   store i32 %int.and_assign1, ptr %a, align 4, !dbg !37
+// CHECK:STDOUT:   ret void, !dbg !38
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestOr.Main(ptr %a, i32 %b) !dbg !39 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.or_assign = load i32, ptr %a, align 4, !dbg !40
+// CHECK:STDOUT:   %int.or_assign1 = or i32 %int.or_assign, %b, !dbg !40
+// CHECK:STDOUT:   store i32 %int.or_assign1, ptr %a, align 4, !dbg !40
+// CHECK:STDOUT:   ret void, !dbg !41
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestXor.Main(ptr %a, i32 %b) !dbg !42 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.xor_assign = load i32, ptr %a, align 4, !dbg !43
+// CHECK:STDOUT:   %int.xor_assign1 = xor i32 %int.xor_assign, %b, !dbg !43
+// CHECK:STDOUT:   store i32 %int.xor_assign1, ptr %a, align 4, !dbg !43
+// CHECK:STDOUT:   ret void, !dbg !44
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestLeftShift.Main(ptr %a, i32 %b) !dbg !45 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.left_shift_assign = load i32, ptr %a, align 4, !dbg !46
+// CHECK:STDOUT:   %int.left_shift_assign1 = shl i32 %int.left_shift_assign, %b, !dbg !46
+// CHECK:STDOUT:   store i32 %int.left_shift_assign1, ptr %a, align 4, !dbg !46
+// CHECK:STDOUT:   ret void, !dbg !47
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestArithmeticRightShift.Main(ptr %a, i32 %b) !dbg !48 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.right_shift_assign = load i32, ptr %a, align 4, !dbg !49
+// CHECK:STDOUT:   %int.right_shift_assign1 = ashr i32 %int.right_shift_assign, %b, !dbg !49
+// CHECK:STDOUT:   store i32 %int.right_shift_assign1, ptr %a, align 4, !dbg !49
+// CHECK:STDOUT:   ret void, !dbg !50
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CTestLogicalRightShift.Main(ptr %a, i32 %b) !dbg !51 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %int.right_shift_assign = load i32, ptr %a, align 4, !dbg !52
+// CHECK:STDOUT:   %int.right_shift_assign1 = lshr i32 %int.right_shift_assign, %b, !dbg !52
+// CHECK:STDOUT:   store i32 %int.right_shift_assign1, ptr %a, align 4, !dbg !52
+// CHECK:STDOUT:   ret void, !dbg !53
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
+// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
+// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
+// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+// CHECK:STDOUT: !3 = !DIFile(filename: "compound_assign.carbon", directory: "")
+// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "TestSAdd", linkageName: "_CTestSAdd.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
+// CHECK:STDOUT: !6 = !{}
+// CHECK:STDOUT: !7 = !DILocation(line: 5, column: 32, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 5, column: 1, scope: !4)
+// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "TestSSub", linkageName: "_CTestSSub.Main", scope: null, file: !3, line: 8, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !10 = !DILocation(line: 8, column: 32, scope: !9)
+// CHECK:STDOUT: !11 = !DILocation(line: 8, column: 1, scope: !9)
+// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "TestSMul", linkageName: "_CTestSMul.Main", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !13 = !DILocation(line: 11, column: 32, scope: !12)
+// CHECK:STDOUT: !14 = !DILocation(line: 11, column: 1, scope: !12)
+// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "TestSDiv", linkageName: "_CTestSDiv.Main", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !16 = !DILocation(line: 14, column: 32, scope: !15)
+// CHECK:STDOUT: !17 = !DILocation(line: 14, column: 1, scope: !15)
+// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "TestSMod", linkageName: "_CTestSMod.Main", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !19 = !DILocation(line: 17, column: 32, scope: !18)
+// CHECK:STDOUT: !20 = !DILocation(line: 17, column: 1, scope: !18)
+// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "TestUAdd", linkageName: "_CTestUAdd.Main", scope: null, file: !3, line: 20, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !22 = !DILocation(line: 20, column: 32, scope: !21)
+// CHECK:STDOUT: !23 = !DILocation(line: 20, column: 1, scope: !21)
+// CHECK:STDOUT: !24 = distinct !DISubprogram(name: "TestUSub", linkageName: "_CTestUSub.Main", scope: null, file: !3, line: 23, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !25 = !DILocation(line: 23, column: 32, scope: !24)
+// CHECK:STDOUT: !26 = !DILocation(line: 23, column: 1, scope: !24)
+// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "TestUMul", linkageName: "_CTestUMul.Main", scope: null, file: !3, line: 26, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !28 = !DILocation(line: 26, column: 32, scope: !27)
+// CHECK:STDOUT: !29 = !DILocation(line: 26, column: 1, scope: !27)
+// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "TestUDiv", linkageName: "_CTestUDiv.Main", scope: null, file: !3, line: 29, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !31 = !DILocation(line: 29, column: 32, scope: !30)
+// CHECK:STDOUT: !32 = !DILocation(line: 29, column: 1, scope: !30)
+// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "TestUMod", linkageName: "_CTestUMod.Main", scope: null, file: !3, line: 32, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !34 = !DILocation(line: 32, column: 32, scope: !33)
+// CHECK:STDOUT: !35 = !DILocation(line: 32, column: 1, scope: !33)
+// CHECK:STDOUT: !36 = distinct !DISubprogram(name: "TestAnd", linkageName: "_CTestAnd.Main", scope: null, file: !3, line: 35, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !37 = !DILocation(line: 35, column: 31, scope: !36)
+// CHECK:STDOUT: !38 = !DILocation(line: 35, column: 1, scope: !36)
+// CHECK:STDOUT: !39 = distinct !DISubprogram(name: "TestOr", linkageName: "_CTestOr.Main", scope: null, file: !3, line: 38, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !40 = !DILocation(line: 38, column: 30, scope: !39)
+// CHECK:STDOUT: !41 = !DILocation(line: 38, column: 1, scope: !39)
+// CHECK:STDOUT: !42 = distinct !DISubprogram(name: "TestXor", linkageName: "_CTestXor.Main", scope: null, file: !3, line: 41, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !43 = !DILocation(line: 41, column: 31, scope: !42)
+// CHECK:STDOUT: !44 = !DILocation(line: 41, column: 1, scope: !42)
+// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "TestLeftShift", linkageName: "_CTestLeftShift.Main", scope: null, file: !3, line: 44, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !46 = !DILocation(line: 44, column: 37, scope: !45)
+// CHECK:STDOUT: !47 = !DILocation(line: 44, column: 1, scope: !45)
+// CHECK:STDOUT: !48 = distinct !DISubprogram(name: "TestArithmeticRightShift", linkageName: "_CTestArithmeticRightShift.Main", scope: null, file: !3, line: 47, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !49 = !DILocation(line: 47, column: 48, scope: !48)
+// CHECK:STDOUT: !50 = !DILocation(line: 47, column: 1, scope: !48)
+// CHECK:STDOUT: !51 = distinct !DISubprogram(name: "TestLogicalRightShift", linkageName: "_CTestLogicalRightShift.Main", scope: null, file: !3, line: 50, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !52 = !DILocation(line: 50, column: 45, scope: !51)
+// CHECK:STDOUT: !53 = !DILocation(line: 50, column: 1, scope: !51)
 // CHECK:STDOUT: ; ModuleID = 'mixed_shift.carbon'
 // CHECK:STDOUT: source_filename = "mixed_shift.carbon"
 // CHECK:STDOUT:

+ 97 - 1
toolchain/sem_ir/builtin_function_kind.cpp

@@ -34,6 +34,9 @@ struct ValidateState {
   TypeId type_params[MaxTypeParams] = {TypeId::None, TypeId::None};
 };
 
+template <typename TypeConstraint>
+auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool;
+
 // Constraint that a type is generic type parameter `I` of the builtin,
 // satisfying `TypeConstraint`. See ValidateSignature for details.
 template <int I, typename TypeConstraint>
@@ -63,6 +66,20 @@ struct BuiltinType {
   }
 };
 
+// Constraint that a type is a pointer to another type. See ValidateSignature
+// for details.
+template <typename PointeeT>
+struct PointerTo {
+  static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id)
+      -> bool {
+    if (!sem_ir.types().Is<SemIR::PointerType>(type_id)) {
+      return false;
+    }
+    return SemIR::Check<PointeeT>(sem_ir, state,
+                                  sem_ir.GetPointeeType(type_id));
+  }
+};
+
 // Constraint that a type is `()`, used as the return type of builtin functions
 // with no return value.
 struct NoReturn {
@@ -219,6 +236,10 @@ using IntU = TypeParam<1, AnyInt>;
 // generic type parameter that is constrained to be a sized integer type.
 using SizedIntT = TypeParam<0, AnySizedInt>;
 
+// Convenience name used in the builtin type signatures below for a second
+// generic type parameter that is constrained to be a sized integer type.
+using SizedIntU = TypeParam<1, AnySizedInt>;
+
 // Convenience name used in the builtin type signatures below for a first
 // generic type parameter that is constrained to be an float type.
 using FloatT = TypeParam<0, AnyFloat>;
@@ -337,10 +358,85 @@ constexpr BuiltinInfo IntXor = {"int.xor",
 constexpr BuiltinInfo IntLeftShift = {
     "int.left_shift", ValidateSignature<auto(IntT, IntU)->IntT>};
 
-// "int.left_shift": integer right shift.
+// "int.right_shift": integer right shift.
 constexpr BuiltinInfo IntRightShift = {
     "int.right_shift", ValidateSignature<auto(IntT, IntU)->IntT>};
 
+// "int.sadd_assign": integer in-place addition.
+constexpr BuiltinInfo IntSAddAssign = {
+    "int.sadd_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.ssub_assign": integer in-place subtraction.
+constexpr BuiltinInfo IntSSubAssign = {
+    "int.ssub_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.smul_assign": integer in-place multiplication.
+constexpr BuiltinInfo IntSMulAssign = {
+    "int.smul_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.sdiv_assign": integer in-place division.
+constexpr BuiltinInfo IntSDivAssign = {
+    "int.sdiv_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.smod_assign": integer in-place modulo.
+constexpr BuiltinInfo IntSModAssign = {
+    "int.smod_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.uadd_assign": unsigned integer in-place addition.
+constexpr BuiltinInfo IntUAddAssign = {
+    "int.uadd_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.usub_assign": unsigned integer in-place subtraction.
+constexpr BuiltinInfo IntUSubAssign = {
+    "int.usub_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.umul_assign": unsigned integer in-place multiplication.
+constexpr BuiltinInfo IntUMulAssign = {
+    "int.umul_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.udiv_assign": unsigned integer in-place division.
+constexpr BuiltinInfo IntUDivAssign = {
+    "int.udiv_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.mod_assign": integer in-place modulo.
+constexpr BuiltinInfo IntUModAssign = {
+    "int.umod_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.and_assign": integer in-place bitwise and.
+constexpr BuiltinInfo IntAndAssign = {
+    "int.and_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.or_assign": integer in-place bitwise or.
+constexpr BuiltinInfo IntOrAssign = {
+    "int.or_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.xor_assign": integer in-place bitwise xor.
+constexpr BuiltinInfo IntXorAssign = {
+    "int.xor_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
+
+// "int.left_shift_assign": integer in-place left shift.
+constexpr BuiltinInfo IntLeftShiftAssign = {
+    "int.left_shift_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntU)->NoReturn>};
+
+// "int.right_shift_assign": integer in-place right shift.
+constexpr BuiltinInfo IntRightShiftAssign = {
+    "int.right_shift_assign",
+    ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntU)->NoReturn>};
+
 // "int.eq": integer equality comparison.
 constexpr BuiltinInfo IntEq = {"int.eq",
                                ValidateSignature<auto(IntT, IntU)->Bool>};

+ 17 - 0
toolchain/sem_ir/builtin_function_kind.def

@@ -68,6 +68,23 @@ CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntLessEq)
 CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntGreater)
 CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntGreaterEq)
 
+// Integer compound assignment.
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntSAddAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntSSubAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntSMulAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntSDivAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntSModAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntUAddAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntUSubAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntUMulAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntUDivAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntUModAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntAndAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntOrAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntXorAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntLeftShiftAssign)
+CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntRightShiftAssign)
+
 // Float arithmetic.
 CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatNegate)
 CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatAdd)