Segmentation fault when emitting gpu dialect with python binding

Hi folks,

I am encountered some issues when trying to emit gpu dialect with python binding.

This snippet

with Context(), Location.unknown():
    module = Module.create()
    with InsertionPoint(module.body):
        # gpu.GPUModuleOp(operation: object)
        # Cannot find any clue about what should be passed to above API.
        # Use general operation creation API instead
        gpu_module = Operation.create(
            name="gpu.module",
            results=[], operands=[],
            attributes={"sym_name": StringAttr.get("ops")},
            successors=None,
            regions=1
        )

        gpu_module_block = Block.create_at_start(gpu_module.body)
        with InsertionPoint(gpu_module_block):
            gpu.ModuleEndOp()

        with InsertionPoint.at_block_terminator(gpu_module_block):
            # gpu.GPUFuncOp(operation: object)
            # Cannot find any clue about what should be passed to above API.
            # Use general operation creation API instead
            f = Operation.create(
                name="gpu.func",
                results=[], operands=[],
                attributes={
                    "sym_name": StringAttr.get("kernel"),
                    "function_type": TypeAttr.get(FunctionType.get([], [])),
                },
                successors=None,
                regions=1
            )
            with InsertionPoint(Block.create_at_start(f.regions[0])):
                gpu.ReturnOp([])

print(module)

gives me a segmentation fault, but follows are workable.

with Context(), Location.unknown():
    module = Module.create()
    with InsertionPoint(module.body):
        # gpu.GPUModuleOp(operation: object)
        # Cannot find any clue about what should be passed to above API.
        # Use general operation creation API instead
        gpu_module = Operation.create(
            name="gpu.module",
            results=[], operands=[],
            attributes={"sym_name": StringAttr.get("ops")},
            successors=None,
            regions=1
        )

        gpu_module_block = Block.create_at_start(gpu_module.body)
        with InsertionPoint(gpu_module_block):
            gpu.ModuleEndOp()

print(module)

gives

module {
  gpu.module @ops {
  }
}

and

with Context(), Location.unknown():
    module = Module.create()
    with InsertionPoint(module.body):
        # gpu.GPUModuleOp(operation: object)
        # Cannot find any clue about which operation object should be passed to above API.
        # Use general operation creation API instead
        gpu_module = Operation.create(
            name="gpu.module",
            results=[], operands=[],
            attributes={"sym_name": StringAttr.get("ops")},
            successors=None,
            regions=1
        )

        gpu_module_block = Block.create_at_start(gpu_module.body)
        with InsertionPoint(gpu_module_block):
            gpu.ModuleEndOp()

        with InsertionPoint.at_block_terminator(gpu_module_block):
            f = func.FuncOp("kernel", ([], []))
            with InsertionPoint(f.add_entry_block()):
                func.ReturnOp([])

print(module)

gives

module {
  gpu.module @ops {
    func.func @kernel() {
      return
    }
  }
}

and

with Context(), Location.unknown():
    module = Module.create()
    with InsertionPoint(module.body):
        # gpu.GPUModuleOp(operation: object)
        # Cannot find any clue about what should be passed to above API.
        # Use general operation creation API instead
        gpu_module = Operation.create(
            name="gpu.module",
            results=[], operands=[],
            attributes={"sym_name": StringAttr.get("ops")},
            successors=None,
            regions=1
        )

        gpu_module_block = Block.create_at_start(gpu_module.body)
        with InsertionPoint(gpu_module_block):
            gpu.ModuleEndOp()

        with InsertionPoint.at_block_terminator(gpu_module_block):
            f = Operation.create(
                name="func.func",
                results=[], operands=[],
                attributes={
                    "sym_name": StringAttr.get("kernel"),
                    "function_type": TypeAttr.get(FunctionType.get([], [])),
                },
                successors=None,
                regions=1
            )
            with InsertionPoint(Block.create_at_start(f.regions[0])):
                func.ReturnOp([])

print(module)

also gives

module {
  gpu.module @ops {
    func.func @kernel() {
      return
    }
  }
}

Ddi I miss anything?

You can run python under gdb to debug segfaults. Here it seems like the issue is in gpu.func’s verifier, which crashes at this line because there’s no workgroup_attributions attribute on the op: llvm-project/GPUDialect.cpp at 77b202f974faa82679d9984aeef58355dc80ba80 · llvm/llvm-project · GitHub

Thanks for the kind help! It works. BTW, do you have any suggestions about using GPUModuleOp and GPUFuncOP to emit gpu dialect? General Operation.create seems not easy to use, and a little bit tricky for beginners like me.

I suspect that the people doing real work with the GPU dialect are using the c++ interface. To be more usable, it needs extensions apis added. If you want to contribute, look at what was done for the func dialect. Using Operation.create is a backdoor for directly creating the low level representation, and if you are finding that you need to use it, it is a sign that work had not been done to define the API for the dialect properly – contributions welcome, but there are not general answers on how to do that (it varies based on judgment and understanding of the core data structures).

This is still there at HEAD, can someone file a bug so we fix this?
(@ftynse this is your code I think)

filed here: [mlir][gpu] Segfault in gpu.func verifier on invalid input · Issue #58045 · llvm/llvm-project · GitHub