Skip to content

Conversation

RKSimon
Copy link
Collaborator

@RKSimon RKSimon commented Aug 28, 2025

Users of interp__builtin_elementwise_int_binop are going to be very well defined, we can use a simple callback mechanism (including existing llvm::APIntOps static methods) to perform the evaluation and avoid a repeated switch statement.

Hopefully this will help keep interp__builtin_elementwise_int_binop clean as we add more uses

…llback instead of repeated switch statement

Users of interp__builtin_elementwise_int_binop are going to be very well defined, we can use a simple callback mechanism (including existing llvm::APIntOps static methods) to perform the evaluation and avoid a repeated switch statement.

Hopefully this will help keep interp__builtin_elementwise_int_binop clean as we add more uses
@RKSimon RKSimon requested a review from tbaederr August 28, 2025 17:28
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:bytecode Issues for the clang bytecode constexpr interpreter labels Aug 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 28, 2025

@llvm/pr-subscribers-clang

Author: Simon Pilgrim (RKSimon)

Changes

Users of interp__builtin_elementwise_int_binop are going to be very well defined, we can use a simple callback mechanism (including existing llvm::APIntOps static methods) to perform the evaluation and avoid a repeated switch statement.

Hopefully this will help keep interp__builtin_elementwise_int_binop clean as we add more uses


Full diff: https://github.com/llvm/llvm-project/pull/155891.diff

1 Files Affected:

  • (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+46-70)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 765c692991645..eba0b25997699 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2548,9 +2548,9 @@ static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC,
   return true;
 }
 
-static bool interp__builtin_elementwise_int_binop(InterpState &S, CodePtr OpPC,
-                                                  const CallExpr *Call,
-                                                  unsigned BuiltinID) {
+static bool interp__builtin_elementwise_int_binop(
+    InterpState &S, CodePtr OpPC, const CallExpr *Call, unsigned BuiltinID,
+    llvm::function_ref<APInt(const APSInt &, const APSInt &)> Fn) {
   assert(Call->getNumArgs() == 2);
 
   // Single integer case.
@@ -2560,15 +2560,7 @@ static bool interp__builtin_elementwise_int_binop(InterpState &S, CodePtr OpPC,
         S.Stk, *S.getContext().classify(Call->getArg(1)->getType()));
     APSInt LHS = popToAPSInt(
         S.Stk, *S.getContext().classify(Call->getArg(0)->getType()));
-    APInt Result;
-    if (BuiltinID == Builtin::BI__builtin_elementwise_add_sat) {
-      Result = LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS);
-    } else if (BuiltinID == Builtin::BI__builtin_elementwise_sub_sat) {
-      Result = LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS);
-    } else {
-      llvm_unreachable("Wrong builtin ID");
-    }
-
+    APInt Result = Fn(LHS, RHS);
     pushInteger(S, APSInt(std::move(Result), !LHS.isSigned()), Call->getType());
     return true;
   }
@@ -2587,8 +2579,6 @@ static bool interp__builtin_elementwise_int_binop(InterpState &S, CodePtr OpPC,
   const Pointer &LHS = S.Stk.pop<Pointer>();
   const Pointer &Dst = S.Stk.peek<Pointer>();
   PrimType ElemT = *S.getContext().classify(VT->getElementType());
-  bool DestUnsigned =
-      VT->getElementType()->isUnsignedIntegerOrEnumerationType();
   unsigned NumElems = VT->getNumElements();
   for (unsigned I = 0; I != NumElems; ++I) {
     APSInt Elem1;
@@ -2598,61 +2588,9 @@ static bool interp__builtin_elementwise_int_binop(InterpState &S, CodePtr OpPC,
       Elem2 = RHS.elem<T>(I).toAPSInt();
     });
 
-    APSInt Result;
-    switch (BuiltinID) {
-    case Builtin::BI__builtin_elementwise_add_sat:
-      Result = APSInt(Elem1.isSigned() ? Elem1.sadd_sat(Elem2)
-                                       : Elem1.uadd_sat(Elem2),
-                      Call->getType()->isUnsignedIntegerOrEnumerationType());
-      break;
-    case Builtin::BI__builtin_elementwise_sub_sat:
-      Result = APSInt(Elem1.isSigned() ? Elem1.ssub_sat(Elem2)
-                                       : Elem1.usub_sat(Elem2),
-                      Call->getType()->isUnsignedIntegerOrEnumerationType());
-      break;
-    case clang::X86::BI__builtin_ia32_pmulhuw128:
-    case clang::X86::BI__builtin_ia32_pmulhuw256:
-    case clang::X86::BI__builtin_ia32_pmulhuw512:
-      Result = APSInt(llvm::APIntOps::mulhu(Elem1, Elem2),
-                      /*isUnsigned=*/true);
-      break;
-    case clang::X86::BI__builtin_ia32_pmulhw128:
-    case clang::X86::BI__builtin_ia32_pmulhw256:
-    case clang::X86::BI__builtin_ia32_pmulhw512:
-      Result = APSInt(llvm::APIntOps::mulhs(Elem1, Elem2),
-                      /*isUnsigned=*/false);
-      break;
-    case clang::X86::BI__builtin_ia32_psllv2di:
-    case clang::X86::BI__builtin_ia32_psllv4di:
-    case clang::X86::BI__builtin_ia32_psllv4si:
-    case clang::X86::BI__builtin_ia32_psllv8si:
-      if (Elem2.uge(Elem2.getBitWidth())) {
-        Result = APSInt(APInt::getZero(Elem2.getBitWidth()), DestUnsigned);
-        break;
-      }
-      Result = APSInt(Elem1.shl(Elem2.getZExtValue()), DestUnsigned);
-      break;
-    case clang::X86::BI__builtin_ia32_psrav4si:
-    case clang::X86::BI__builtin_ia32_psrav8si:
-      if (Elem2.uge(Elem2.getBitWidth())) {
-        Result = APSInt(Elem1.ashr(Elem2.getBitWidth() - 1), DestUnsigned);
-        break;
-      }
-      Result = APSInt(Elem1.ashr(Elem2.getZExtValue()), DestUnsigned);
-      break;
-    case clang::X86::BI__builtin_ia32_psrlv2di:
-    case clang::X86::BI__builtin_ia32_psrlv4di:
-    case clang::X86::BI__builtin_ia32_psrlv4si:
-    case clang::X86::BI__builtin_ia32_psrlv8si:
-      if (Elem2.uge(Elem2.getBitWidth())) {
-        Result = APSInt(APInt::getZero(Elem2.getBitWidth()), DestUnsigned);
-        break;
-      }
-      Result = APSInt(Elem1.lshr(Elem2.getZExtValue()), DestUnsigned);
-      break;
-    default:
-      llvm_unreachable("Wrong builtin ID");
-    }
+    APSInt Result =
+        APSInt(Fn(Elem1, Elem2),
+               Call->getType()->isUnsignedIntegerOrEnumerationType());
 
     INT_TYPE_SWITCH_NO_BOOL(ElemT,
                             { Dst.elem<T>(I) = static_cast<T>(Result); });
@@ -3289,24 +3227,62 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
     return interp__builtin_is_within_lifetime(S, OpPC, Call);
 
   case Builtin::BI__builtin_elementwise_add_sat:
+    return interp__builtin_elementwise_int_binop(
+        S, OpPC, Call, BuiltinID, [](const APSInt &LHS, const APSInt &RHS) {
+          return LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS);
+        });
+
   case Builtin::BI__builtin_elementwise_sub_sat:
+    return interp__builtin_elementwise_int_binop(
+        S, OpPC, Call, BuiltinID, [](const APSInt &LHS, const APSInt &RHS) {
+          return LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS);
+        });
+
   case clang::X86::BI__builtin_ia32_pmulhuw128:
   case clang::X86::BI__builtin_ia32_pmulhuw256:
   case clang::X86::BI__builtin_ia32_pmulhuw512:
+    return interp__builtin_elementwise_int_binop(S, OpPC, Call, BuiltinID,
+                                                 llvm::APIntOps::mulhu);
+
   case clang::X86::BI__builtin_ia32_pmulhw128:
   case clang::X86::BI__builtin_ia32_pmulhw256:
   case clang::X86::BI__builtin_ia32_pmulhw512:
+    return interp__builtin_elementwise_int_binop(S, OpPC, Call, BuiltinID,
+                                                 llvm::APIntOps::mulhs);
+
   case clang::X86::BI__builtin_ia32_psllv2di:
   case clang::X86::BI__builtin_ia32_psllv4di:
   case clang::X86::BI__builtin_ia32_psllv4si:
   case clang::X86::BI__builtin_ia32_psllv8si:
+    return interp__builtin_elementwise_int_binop(
+        S, OpPC, Call, BuiltinID, [](const APSInt &LHS, const APSInt &RHS) {
+          if (RHS.uge(RHS.getBitWidth())) {
+            return APInt::getZero(RHS.getBitWidth());
+          }
+          return LHS.shl(RHS.getZExtValue());
+        });
+
   case clang::X86::BI__builtin_ia32_psrav4si:
   case clang::X86::BI__builtin_ia32_psrav8si:
+    return interp__builtin_elementwise_int_binop(
+        S, OpPC, Call, BuiltinID, [](const APSInt &LHS, const APSInt &RHS) {
+          if (RHS.uge(RHS.getBitWidth())) {
+            return LHS.ashr(RHS.getBitWidth() - 1);
+          }
+          return LHS.ashr(RHS.getZExtValue());
+        });
+
   case clang::X86::BI__builtin_ia32_psrlv2di:
   case clang::X86::BI__builtin_ia32_psrlv4di:
   case clang::X86::BI__builtin_ia32_psrlv4si:
   case clang::X86::BI__builtin_ia32_psrlv8si:
-    return interp__builtin_elementwise_int_binop(S, OpPC, Call, BuiltinID);
+    return interp__builtin_elementwise_int_binop(
+        S, OpPC, Call, BuiltinID, [](const APSInt &LHS, const APSInt &RHS) {
+          if (RHS.uge(RHS.getBitWidth())) {
+            return APInt::getZero(RHS.getBitWidth());
+          }
+          return LHS.lshr(RHS.getZExtValue());
+        });
 
   case Builtin::BI__builtin_elementwise_max:
   case Builtin::BI__builtin_elementwise_min:

@tbaederr
Copy link
Contributor

[clang][bytecode] in the title please.

Does this also work with the immediate shifts from #155542?

@RKSimon RKSimon changed the title [Clang][Interp] interp__builtin_elementwise_int_binop - use APSInt callback instead of repeated switch statement [Clang][bytecode] interp__builtin_elementwise_int_binop - use APSInt callback instead of repeated switch statement Aug 29, 2025
@RKSimon
Copy link
Collaborator Author

RKSimon commented Aug 29, 2025

Unlikely - it's really just for elementwise patterns. But as a followup we could tweak the implementation so a scalar RHS is repeatedly used in the Fn call for every LHS element?

@RKSimon
Copy link
Collaborator Author

RKSimon commented Aug 29, 2025

Ideally I'd like to get this in before #155542 as that patch isn't ready yet - I can then help @Arghnews refactor (I have to do something similar for the rotate instrinsics as well).

@RKSimon RKSimon enabled auto-merge (squash) August 29, 2025 11:25
@RKSimon RKSimon merged commit fcc7867 into llvm:main Aug 29, 2025
9 checks passed
@RKSimon RKSimon deleted the clang-interp-binop-callback branch August 29, 2025 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:bytecode Issues for the clang bytecode constexpr interpreter clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants