[clang] [CIR] Upstream VectorType __builtin_astype (PR #192859)

via cfe-commits cfe-commits at lists.llvm.org
Sun Apr 19 10:18:33 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangir

Author: Amr Hesham (AmrDeveloper)

<details>
<summary>Changes</summary>

Upstream support for VectorType __builtin_astype

Issue #<!-- -->192311

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


2 Files Affected:

- (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+90-2) 
- (added) clang/test/CIR/CodeGenOpenCL/as_type.cl (+54) 


``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 298baaba8d3e2..d2866d5db10ee 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1422,11 +1422,99 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
     return {};
   }
 
-  mlir::Value VisitAsTypeExpr(AsTypeExpr *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(), "ScalarExprEmitter: as type");
+  // Create cast instructions for converting MLIR value \p Src to MLIR type \p
+  // DstTy. \p Src has the same size as \p DstTy. Both are single value types
+  // but could be scalar or vectors of different lengths, and either can be
+  // pointer.
+  //
+  // There are 4 cases:
+  // 1. non-pointer -> non-pointer  : needs 1 bitcast
+  // 2. pointer -> pointer          : needs 1 bitcast or addrspacecast
+  // 3. pointer -> non-pointer
+  //   a) pointer -> intptr_t       : needs 1 ptrtoint
+  //   b) pointer -> non-intptr_t   : needs 1 ptrtoint then 1 bitcast
+  // 4. non-pointer -> pointer
+  //   a) intptr_t -> pointer       : needs 1 inttoptr
+  //   b) non-intptr_t -> pointer   : needs 1 bitcast then 1 inttoptr
+  //
+  // Note: for cases 3b and 4b two casts are required since LLVM casts do not
+  // allow casting directly between pointer types and non-integer non-pointer
+  // types.
+  mlir::Value createCastsForTypeOfSameSize(mlir::Value src, mlir::Type dstTy) {
+    mlir::Type srcTy = src.getType();
+
+    // Case 1.
+    if (!isa<cir::PointerType>(srcTy) && !isa<cir::PointerType>(dstTy))
+      return builder.createBitcast(src, dstTy);
+
+    // Case 2.
+    if (isa<cir::PointerType>(srcTy) && isa<cir::PointerType>(dstTy)) {
+      cgf.cgm.errorNYI(
+          "ScalarExprEmitter: createCastsForTypeOfSameSize Case 2");
+      return {};
+    }
+
+    // Case 3.
+    if (isa<cir::PointerType>(srcTy) && !isa<cir::PointerType>(dstTy)) {
+      cgf.cgm.errorNYI(
+          "ScalarExprEmitter: createCastsForTypeOfSameSize Case 3");
+      return {};
+    }
+
+    // Case 4b.
+    if (srcTy.isInteger()) {
+      cgf.cgm.errorNYI(
+          "ScalarExprEmitter: createCastsForTypeOfSameSize Case 4b");
+      return {};
+    }
+
+    // Cases 4a and 4b.
+    cgf.cgm.errorNYI(
+        "ScalarExprEmitter: createCastsForTypeOfSameSize Cases 4a and 4b");
     return {};
   }
 
+  mlir::Value VisitAsTypeExpr(AsTypeExpr *e) {
+    mlir::Value src = cgf.emitScalarExpr(e->getSrcExpr());
+    mlir::Type srcTy = src.getType();
+    mlir::Type dstTy = cgf.convertType(e->getType());
+
+    unsigned numElementsSrc = isa<cir::VectorType>(srcTy)
+                                  ? cast<cir::VectorType>(srcTy).getSize()
+                                  : 0;
+    unsigned numElementsDst = isa<cir::VectorType>(dstTy)
+                                  ? cast<cir::VectorType>(dstTy).getSize()
+                                  : 0;
+
+    // Use bit vector expansion for ext_vector_type boolean vectors.
+    if (e->getType()->isExtVectorBoolType()) {
+      cgf.cgm.errorNYI(e->getSourceRange(),
+                       "ScalarExprEmitter: VisitAsTypeExpr ExtVectorBoolType");
+      return {};
+    }
+
+    // Going from vec3 to non-vec3 is a special case and requires a shuffle
+    // vector to get a vec4, then a bitcast if the target type is different.
+    if (numElementsSrc == 3 && numElementsDst != 3) {
+      cgf.cgm.errorNYI(e->getSourceRange(),
+                       "ScalarExprEmitter: VisitAsTypeExpr numElemsSrc = 3, "
+                       "numElemsDst = 3");
+      return {};
+    }
+
+    // Going from non-vec3 to vec3 is a special case and requires a bitcast
+    // to vec4 if the original type is not vec4, then a shuffle vector to
+    // get a vec3.
+    if (numElementsSrc != 3 && numElementsDst == 3) {
+      cgf.cgm.errorNYI(e->getSourceRange(),
+                       "ScalarExprEmitter: VisitAsTypeExpr numElemsSrc != 3, "
+                       "numElemsDst = 3");
+      return {};
+    }
+
+    return createCastsForTypeOfSameSize(src, dstTy);
+  }
+
   mlir::Value VisitAtomicExpr(AtomicExpr *e) {
     return cgf.emitAtomicExpr(e).getValue();
   }
diff --git a/clang/test/CIR/CodeGenOpenCL/as_type.cl b/clang/test/CIR/CodeGenOpenCL/as_type.cl
new file mode 100644
index 0000000000000..f314b1dced5b5
--- /dev/null
+++ b/clang/test/CIR/CodeGenOpenCL/as_type.cl
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 %s -fclangir -emit-cir -triple spir-unknown-unknown -o %t.cir
+// RUN: FileCheck %s --input-file=%t.cir --check-prefix=CIR
+
+// RUN: %clang_cc1 %s -fclangir -emit-llvm -triple spir-unknown-unknown -o %t.ll
+// RUN: FileCheck %s --input-file=%t.ll --check-prefix=LLVM
+
+// RUN: %clang_cc1 %s -emit-llvm -triple spir-unknown-unknown -o %t.ll
+// RUN: FileCheck %s --input-file=%t.ll --check-prefix=OGCG
+
+typedef __attribute__(( ext_vector_type(4) )) char char4;
+
+char4 f4(int x) {
+  return __builtin_astype(x, char4);
+}
+
+// CIR: cir.func {{.*}} @f4
+// CIR:   %[[X_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
+// CIR:   %[[RET_ADDR:.*]] = cir.alloca !cir.vector<4 x !s8i>, !cir.ptr<!cir.vector<4 x !s8i>>, ["__retval"]
+// CIR:   cir.store %{{.*}}, %[[X_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[TMP_X:.*]] = cir.load {{.*}} %[[X_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR:   %[[X_V4_I8:.*]] = cir.cast bitcast %[[TMP_X]] : !s32i -> !cir.vector<4 x !s8i>
+// CIR:   cir.store %[[X_V4_I8]], %[[RET_ADDR]] : !cir.vector<4 x !s8i>, !cir.ptr<!cir.vector<4 x !s8i>>
+// CIR:   %[[TMP_RET:.*]] = cir.load %[[RET_ADDR]] : !cir.ptr<!cir.vector<4 x !s8i>>, !cir.vector<4 x !s8i>
+// CIR:   cir.return %[[TMP_RET]] : !cir.vector<4 x !s8i>
+
+// LLVM: define {{.*}} <4 x i8> @f4
+// LLVM:  %[[RET:.*]] = bitcast i32 %{{.*}} to <4 x i8>
+// LLVM:  ret <4 x i8> %[[RET]]
+
+// OGCG: define {{.*}} <4 x i8> @f4
+// OGCG:  %[[RET:.*]] = bitcast i32 %{{.*}} to <4 x i8>
+// OGCG:  ret <4 x i8> %[[RET]]
+
+int f6(char4 x) {
+  return __builtin_astype(x, int);
+}
+
+// CIR: cir.func {{.*}} @f6
+// CIR:   %[[X_ADDR:.*]] = cir.alloca !cir.vector<4 x !s8i>, !cir.ptr<!cir.vector<4 x !s8i>>, ["x", init]
+// CIR:   %[[RET_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR:   cir.store %{{.*}}, %[[X_ADDR]] : !cir.vector<4 x !s8i>, !cir.ptr<!cir.vector<4 x !s8i>>
+// CIR:   %[[TMP_X:.*]] = cir.load {{.*}} %[[X_ADDR]] : !cir.ptr<!cir.vector<4 x !s8i>>, !cir.vector<4 x !s8i>
+// CIR:   %[[X_S32I:.*]] = cir.cast bitcast %[[TMP_X]] : !cir.vector<4 x !s8i> -> !s32i
+// CIR:   cir.store %[[X_S32I]], %[[RET_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[TMP_RET:.*]] = cir.load %[[RET_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR:   cir.return %[[TMP_RET]] : !s32i
+
+// LLVM: define {{.*}} i32 @f6
+// LLVM:  %[[RET:.*]] = bitcast <4 x i8> %{{.*}} to i32
+// LLVM:  ret i32 %[[RET]]
+
+// OGCG: define {{.*}} i32 @f6
+// OGCG:  %[[RET:.*]] = bitcast <4 x i8> %{{.*}} to i32
+// OGCG:  ret i32 %[[RET]]

``````````

</details>


https://github.com/llvm/llvm-project/pull/192859


More information about the cfe-commits mailing list