Skip to content

Conversation

@mahmood82
Copy link
Contributor

Implement handling for zero-initialization casts to OpenCL opaque types in CIR. This covers cases like event_t e = async_work_group_copy(..., 0).

  • VisitCastExpr: CK_ZeroToOCLOpaqueType now returns a null pointer of the appropriate opaque type instead of llvm_unreachable.
  • CIRGenTypes::convertType: Added proper CIR type conversions for OpenCL opaque types including event, queue, and reserve_id types.
  • Provides consistent CIR representation for OpenCL opaque objects.

Implement handling for zero-initialization casts to OpenCL opaque types
in CIR. This covers cases like `event_t e = async_work_group_copy(..., 0)`.

- `VisitCastExpr`: CK_ZeroToOCLOpaqueType now returns a null pointer of the
  appropriate opaque type instead of `llvm_unreachable`.
- `CIRGenTypes::convertType`: Added proper CIR type conversions for OpenCL
  opaque types including event, queue, and reserve_id types.
- Provides consistent CIR representation for OpenCL opaque objects.
Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a testcase

@mahmood82 mahmood82 force-pushed the users/mahmood/ocl_opaque_types branch from a85d27e to e808d7f Compare November 25, 2025 11:24
@mahmood82
Copy link
Contributor Author

mahmood82 commented Nov 25, 2025

Please add a testcase

I added llvm-project/clang/test/CIR/CodeGen/OpenCL/async_copy.cl. However, it require changes in my PR #2019

In addition I commented out some logic as I keep hitting this assert:

llvm_unreachable("Requires address space cast which is NYI");

@RiverDave claimed it will be fixed in his PR: #1986

Have said this, async_copy.cl as is is enough for the changes I made in this PR.

@mahmood82 mahmood82 force-pushed the users/mahmood/ocl_opaque_types branch from e808d7f to 0eb9550 Compare November 25, 2025 13:40
Copy link
Collaborator

@seven-mile seven-mile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: There's also an issue #802 tracking the design of OpenCL opaque types.

It's not necessary to give a final design for them, but we'd better avoid potential miscompilation here!😉


// CIR: cir.call @_Z21async_work_group_copyPU3AS3iPU3AS1Kim9ocl_event(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) : (!cir.ptr<!s32i, addrspace(offload_local)>, !cir.ptr<!s32i, addrspace(offload_global)>, !u64i, !cir.ptr<!void>) -> !cir.ptr<!void>
// LLVM: call spir_func ptr @_Z21async_work_group_copyPU3AS3iPU3AS1Kim9ocl_event(ptr addrspace(3) %{{.*}}, ptr addrspace(1) %{{.*}}, i64 %{{.*}}, ptr null)
// OG-LLVM: call spir_func target("spirv.Event") @_Z21async_work_group_copyPU3AS3iPU3AS1Kim9ocl_event(ptr addrspace(3) noundef %{{.*}}, ptr addrspace(1) noundef %{{.*}}, i64 noundef %{{.*}}, target("spirv.Event") zeroinitializer No newline at end of file
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a difference between ours and the original LLVM (target("spirv.Event") vs ptr addrspace(0)). Is this expected? (I believe the assertion about address space cast you encountered is also a little alarm for us.)

Simply using ptr addrspace(0) here usually works. But the original RFC of LLVM opaque types mentions that it's actually undefined behavior in LLVM IR.

@RiverDave
Copy link
Collaborator

Please add a testcase

I added llvm-project/clang/test/CIR/CodeGen/OpenCL/async_copy.cl. However, it require changes in my PR #2019

In addition I commented out some logic as I keep hitting this assert:

llvm_unreachable("Requires address space cast which is NYI");

@RiverDave claimed it will be fixed in his PR: #1986

Have said this, async_copy.cl as is, is enough for the changes I made in this PR.

If this ever becomes a huge blocker, I believe it can be implemented using the existing code in the incubator. I'll try to land my PR's from upstream by the end of the week/early next (hopefully).

@bcardosolopes
Copy link
Member

@mahmood82 can you implement this as if #1986 isn't happening? Whenever @RiverDave works on it he'd have to change it to conform to his work. Even if slightly not ideal CIR/CodeGen/OpenCL/async_copy.cl should emit something and have CIR, LLVM and OGCG tests. You can also use assert on missing feature to improve certain aspects while that PR doesn't land

Simply using ptr addrspace(0) here usually works. But the original RFC of LLVM opaque types mentions that it's actually undefined behavior in LLVM IR.

maybe you could do this until it lands?

@mahmood82
Copy link
Contributor Author

#1986 is not a blocker here, and it’s not restricting the main feature of this PR (defining the OCL event type and passing zero to async_work_group_copy). The related assertion also happens on something as simple as int gid = ..., so it is orthogonal. Bottom line: it does not block this PR.

When I generate LLVM IR without CIR in the pipeline, my non-SPIRV64 target produces:

%call = call ptr @_Z21async_work_group_copyPU3AS3iPU3AS1Kij9ocl_event(ptr addrspace(3) noundef %0, ptr addrspace(1) noundef %1, i32 noundef %2, ptr null) #2

Notice that the OpenCL event becomes a plain ptr with no extra attributes.

SPIR-V uses a target-specific path:
CGOpenCLRuntime::convertOpenCLSpecificType ==> CommonSPIRTargetCodeGenInfo::getOpenCLType.

A robust solution for CIR therefore seems to require preserving the OCL type identity through AST->CIR, and letting the CIR->LLVM lowering eventually call into getOpenCLType. To enable that, I need to replace the AST->CIR lowering with something like:

ResultType = cir::OCLEventType::get(Builder.getContext());

(and do the same for the other OpenCL builtin types).

I initially attempted to define such a type in clang/include/clang/CIR/Dialect/IR/CIRTypes.td:

def CIR_OCLEventType : CIR_Type<"OCLEvent", "ocl.event"> {
  let summary = "OpenCL opaque event type";
}

Got this CIR result:

    %7 = "cir.const"() <{value = #cir.ptr<null> : !cir.ptr<!cir.ocl.event, addrspace(offload_private)>}> : () -> !cir.ptr<!cir.ocl.event, addrspace(offload_private)>
    %8 = "cir.cast"(%7) <{kind = 1 : i32}> : (!cir.ptr<!cir.ocl.event, addrspace(offload_private)>) -> !cir.ocl.event
	%9 = "cir.call"(%3, %4, %6, %8) <{ast = #cir.call.expr.ast, callee = @_Z21async_work_group_copyPU3AS3iPU3AS1Kim9ocl_event, calling_conv = 3 : i32, extra_attrs = #cir<extra({convergent 

But that fails verification with:

error: 'cir.cast' op result #0 must be Integer type with arbitrary precision up to a fixed limit or CIR pointer type or CIR type that represents pointer-to-data-member type in C++ or CIR type that represents C++ pointer-to-member-function type or CIR bool type or CIR array type or CIR vector type or CIR function type or CIR void type or CIR record type or CIR exception info or single float type or double float type or f16 type or bf16 type or f80 type or f128 type or long double type or CIR complex type or CIR type that is used for the vptr member of C++ objects, but got '!cir.ocl.event'

I would appreciate guidance on whether this direction is correct, and on how best to complete the addition of the new OCLEventType so that it fits into the existing CIR type system and cast semantics. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants