From e277346f434ed4a79b20bd0976c529da82f3ba5c Mon Sep 17 00:00:00 2001 From: Mahmood Yassin Date: Mon, 17 Nov 2025 10:21:36 +0200 Subject: [PATCH 1/2] [CIR] Emit bitcast for equal-width types Implement VisitAsTypeExpr to lower AsTypeExpr expressions. - Emit an error when source and destination types differ in bitwidth. - When types already match, return the source value (no-op). - Otherwise lower to a CIR bitcast using cir::CastOp with CastKind::bitcast. --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 29 ++++++++++++++++++- .../CIR/CodeGen/OpenCL/vec_int_as_float.cl | 9 ++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 clang/test/CIR/CodeGen/OpenCL/vec_int_as_float.cl diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index b6b114f0e4b9..2f1c719a8b1f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -820,7 +820,34 @@ class ScalarExprEmitter : public StmtVisitor { mlir::Value VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { llvm_unreachable("NYI"); } - mlir::Value VisitAsTypeExpr(AsTypeExpr *E) { llvm_unreachable("NYI"); } + + mlir::Value VisitAsTypeExpr(AsTypeExpr *E) { + mlir::Value src = CGF.emitScalarExpr(E->getSrcExpr()); + QualType qualSrcTy = E->getSrcExpr()->getType(); + QualType qualDstTy = E->getType(); + + // Bitwidth check + unsigned srcBits = CGF.getContext().getTypeSize(qualSrcTy); + unsigned dstBits = CGF.getContext().getTypeSize(qualDstTy); + if (srcBits != dstBits) { + emitError(CGF.getLoc(E->getExprLoc()), + "source and destination must have equal bitwidths: '" + + llvm::Twine(srcBits) + "' vs '" + llvm::Twine(dstBits) + + "'"); + return nullptr; + } + + // No-op if already same type + mlir::Type srcTy = CGF.convertType(qualSrcTy); + mlir::Type dstTy = CGF.convertType(qualDstTy); + if (srcTy == dstTy) + return src; + + // Perform the bitcast + auto loc = CGF.getLoc(E->getExprLoc()); + return Builder.create(loc, dstTy, cir::CastKind::bitcast, src); + } + mlir::Value VisitAtomicExpr(AtomicExpr *E) { return CGF.emitAtomicExpr(E).getScalarVal(); } diff --git a/clang/test/CIR/CodeGen/OpenCL/vec_int_as_float.cl b/clang/test/CIR/CodeGen/OpenCL/vec_int_as_float.cl new file mode 100644 index 000000000000..75d5de1063e0 --- /dev/null +++ b/clang/test/CIR/CodeGen/OpenCL/vec_int_as_float.cl @@ -0,0 +1,9 @@ +// RUN: %clang -target x86_64-unknown-linux-gnu -cl-std=CL3.0 -Xclang -finclude-default-header -Xclang -fclangir -emit-cir %s -o - | FileCheck %s + +float4 test(int4 in) +{ + return as_float4(in); // Bit reinterpretation +} + +// CHECK: [[LOAD:%.*]] = cir.load align(16) %{{.*}} : !cir.ptr>, !cir.vector +// CHECK: %{{.*}} = cir.cast bitcast [[LOAD]] : !cir.vector -> !cir.vector \ No newline at end of file From 411a74a379e43bd3cd72da6e377a54dce33c123a Mon Sep 17 00:00:00 2001 From: Mahmood Yassin Date: Thu, 20 Nov 2025 11:09:26 +0200 Subject: [PATCH 2/2] fix VisitAsTypeExpr to align with OG codegen --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 44 ++++++++++++------- clang/test/CIR/CodeGen/OpenCL/as_type.cl | 43 ++++++++++++++++++ .../CIR/CodeGen/OpenCL/vec_int_as_float.cl | 9 ---- 3 files changed, 72 insertions(+), 24 deletions(-) create mode 100644 clang/test/CIR/CodeGen/OpenCL/as_type.cl delete mode 100644 clang/test/CIR/CodeGen/OpenCL/vec_int_as_float.cl diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 2f1c719a8b1f..cea2cf306d5b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -826,26 +826,40 @@ class ScalarExprEmitter : public StmtVisitor { QualType qualSrcTy = E->getSrcExpr()->getType(); QualType qualDstTy = E->getType(); - // Bitwidth check - unsigned srcBits = CGF.getContext().getTypeSize(qualSrcTy); - unsigned dstBits = CGF.getContext().getTypeSize(qualDstTy); - if (srcBits != dstBits) { - emitError(CGF.getLoc(E->getExprLoc()), - "source and destination must have equal bitwidths: '" + - llvm::Twine(srcBits) + "' vs '" + llvm::Twine(dstBits) + - "'"); - return nullptr; - } - - // No-op if already same type mlir::Type srcTy = CGF.convertType(qualSrcTy); mlir::Type dstTy = CGF.convertType(qualDstTy); + auto loc = CGF.getLoc(E->getExprLoc()); + + unsigned numSrcElems = 0, numDstElems = 0; + if (auto v = dyn_cast(srcTy)) + numSrcElems = v.getSize(); + if (auto v = dyn_cast(dstTy)) + numDstElems = v.getSize(); + + // Use bit vector expansion for ext_vector_type boolean vectors. + if (qualDstTy->isExtVectorBoolType()) { + llvm_unreachable("NYI"); + } + + // 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 (numSrcElems == 3 && numDstElems != 3) { + llvm_unreachable("NYI"); + } + + // 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 (numSrcElems != 3 && numDstElems == 3) { + llvm_unreachable("NYI"); + } + + // If types are identical, return the source if (srcTy == dstTy) return src; - // Perform the bitcast - auto loc = CGF.getLoc(E->getExprLoc()); - return Builder.create(loc, dstTy, cir::CastKind::bitcast, src); + // Otherwise, fallback to CIR bitcast + return cir::CastOp::create(Builder, loc, dstTy, cir::CastKind::bitcast, src); } mlir::Value VisitAtomicExpr(AtomicExpr *E) { diff --git a/clang/test/CIR/CodeGen/OpenCL/as_type.cl b/clang/test/CIR/CodeGen/OpenCL/as_type.cl new file mode 100644 index 000000000000..5a8043b5cf94 --- /dev/null +++ b/clang/test/CIR/CodeGen/OpenCL/as_type.cl @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 %s -cl-std=CL2.0 -fclangir -emit-cir -triple spirv64-unknown-unknown -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll --check-prefix=CIR + +// RUN: %clang_cc1 %s -cl-std=CL2.0 -fclangir -emit-llvm -triple spirv64-unknown-unknown -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll --check-prefix=LLVM + +// RUN: %clang_cc1 %s -cl-std=CL2.0 -emit-llvm -triple spirv64-unknown-unknown -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll --check-prefix=OG-LLVM + +typedef __attribute__(( ext_vector_type(3) )) char char3; +typedef __attribute__(( ext_vector_type(4) )) char char4; +typedef __attribute__(( ext_vector_type(16) )) char char16; +typedef __attribute__(( ext_vector_type(3) )) int int3; + +//CIR: cir.func @f4(%{{.*}}: !s32i loc({{.*}})) -> !cir.vector +//CIR: %[[x:.*]] = cir.load align(4) %{{.*}} : !cir.ptr +//CIR: cir.cast bitcast %[[x]] : !s32i -> !cir.vector +//LLVM: define spir_func <4 x i8> @f4(i32 %[[x:.*]]) +//LLVM: %[[astype:.*]] = bitcast i32 %[[x]] to <4 x i8> +//LLVM-NOT: shufflevector +//LLVM: ret <4 x i8> %[[astype]] +//OG-LLVM: define spir_func noundef <4 x i8> @f4(i32 noundef %[[x:.*]]) +//OG-LLVM: %[[astype:.*]] = bitcast i32 %[[x]] to <4 x i8> +//OG-LLVM-NOT: shufflevector +//OG-LLVM: ret <4 x i8> %[[astype]] +char4 f4(int x) { + return __builtin_astype(x, char4); +} + +//CIR: cir.func @f6(%{{.*}}: !cir.vector loc({{.*}})) -> !s32i +//CIR: %[[x:.*]] = cir.load align(4) %{{.*}} : !cir.ptr, addrspace(offload_private)>, !cir.vector +//CIR: cir.cast bitcast %[[x]] : !cir.vector -> !s32i +//LLVM: define{{.*}} spir_func i32 @f6(<4 x i8> %[[x:.*]]) +//LLVM: %[[astype:.*]] = bitcast <4 x i8> %[[x]] to i32 +//LLVM-NOT: shufflevector +//LLVM: ret i32 %[[astype]] +//OG-LLVM: define{{.*}} spir_func noundef i32 @f6(<4 x i8> noundef %[[x:.*]]) +//OG-LLVM: %[[astype:.*]] = bitcast <4 x i8> %[[x]] to i32 +//OG-LLVM-NOT: shufflevector +//OG-LLVM: ret i32 %[[astype]] +int f6(char4 x) { + return __builtin_astype(x, int); +} \ No newline at end of file diff --git a/clang/test/CIR/CodeGen/OpenCL/vec_int_as_float.cl b/clang/test/CIR/CodeGen/OpenCL/vec_int_as_float.cl deleted file mode 100644 index 75d5de1063e0..000000000000 --- a/clang/test/CIR/CodeGen/OpenCL/vec_int_as_float.cl +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %clang -target x86_64-unknown-linux-gnu -cl-std=CL3.0 -Xclang -finclude-default-header -Xclang -fclangir -emit-cir %s -o - | FileCheck %s - -float4 test(int4 in) -{ - return as_float4(in); // Bit reinterpretation -} - -// CHECK: [[LOAD:%.*]] = cir.load align(16) %{{.*}} : !cir.ptr>, !cir.vector -// CHECK: %{{.*}} = cir.cast bitcast [[LOAD]] : !cir.vector -> !cir.vector \ No newline at end of file