Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73570/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# PaddlePaddle__Paddle-73570

This directory converts Paddle PR #73570 into a SWE-Paddle community task candidate.

## Source

| Field | Value |
| --- | --- |
| Repo | `PaddlePaddle/Paddle` |
| PR | [73570](https://github.com/PaddlePaddle/Paddle/pull/73570) |
| PR title | `[0-size Tensor Job2 No.87] Add 0-size Tensor support for masked_fill` |
| Base commit | `3efb8dbb51547f0235a402135c54ed83c2f12d61` |
| Gold commit | `70574f3ff130128d7cfed5a7bc50f2842137cc98` |
| Merged at | `2025-07-01` |
| Task type | `bug_fix` |
| Resource | CPU |
| Scope | C++ Operator Kernel |

## Summary

Fix `paddle.masked_fill` and `paddle.diag` to correctly handle 0-size tensors in CPU/GPU/XPU kernels by adding early-return logic and fixing gradient shape handling.

## Why This Is A Good SWE-Paddle Candidate

- It is derived from a merged Paddle bug-fix PR rather than a synthetic issue.
- The target behavior is isolated to the C++ operator kernel level and requires rebuilding Paddle from source.
- The failure is deterministic: the base revision fails when processing 0-size tensors due to missing early-return logic and incorrect gradient shape handling.
- The task has clear regression coverage for existing non-zero-size behavior.
- The task runs on CPU and does not require distributed execution, external services, or additional datasets.

## Files

- `proposal.md`: candidate proposal for maintainer triage.
- `instruction.md`: self-contained problem statement for the coding agent.
- `solution/code.patch`: gold implementation patch (C++ kernel changes).
- `tests/test.patch`: tests exposing the target behavior.
- `tests/test.sh`: minimal target test command.
- `environment/README.md`: environment and reproduction notes.

## Verification

```bash
bash tests/test.sh
```

Expected behavior:

| Revision state | Existing behavior (P2P) | masked_fill/diag F2P |
| --- | ---: | ---: |
| Base + `tests/test.patch` | PASS | FAIL |
| Base + `tests/test.patch` + `solution/code.patch` | PASS | PASS |
52 changes: 52 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73570/environment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Environment Notes

## Expected Environment

- Repository: `PaddlePaddle/Paddle`
- Base commit: `3efb8dbb51547f0235a402135c54ed83c2f12d61`
- Gold commit: `70574f3ff130128d7cfed5a7bc50f2842137cc98`
- Resource: CPU
- GPU required: no
- Patch type: C++ kernel (CPU/GPU/XPU backends) + InferMeta + Symbolic Shape
- Python dependencies: PaddlePaddle (source build), NumPy

The verifier should execute against the Paddle source revision represented by the selected patch state. A source build is required since the patch modifies C++ kernel code, InferMeta, and symbolic shape inference.

## Build Instructions

1. Check out `PaddlePaddle/Paddle` at the base commit.
2. Apply `tests/test.patch`.
3. Build Paddle from source (CPU-only build is sufficient):
```bash
mkdir build && cd build
cmake .. -DWITH_GPU=OFF -DWITH_TESTING=ON -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
```
4. Install the built Paddle package.

## Run Order

1. Check out `PaddlePaddle/Paddle` at the base commit.
2. Build and install Paddle from source.
3. Apply `tests/test.patch`.
4. Run the P2P tests; existing non-zero-size behavior should pass.
5. Run the 0-size tensor tests; the target case should fail before the fix.
6. Apply `solution/code.patch`.
7. Rebuild Paddle from source.
8. Reinstall Paddle package.
9. Run `bash tests/test.sh`; all target tests should pass.

## Minimal Test Command

```bash
bash tests/test.sh
```

## Expected Matrix

| Revision state | P2P | masked_fill/diag F2P |
| --- | ---: | ---: |
| Base + test patch | PASS | FAIL |
| Base + test patch + solution patch | PASS | PASS |

No GPU, distributed runtime, external service, or additional dataset is required.
51 changes: 51 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73570/instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 修复 `paddle.masked_fill` 和 `paddle.diag` 对 0-size Tensor 的处理

## 详细描述

当 `paddle.masked_fill(x, mask, value)` 或 `paddle.diag(x)` 的输入为 0-size Tensor 时,当前 CPU/GPU/XPU kernel 实现会直接进入后续计算逻辑,导致 kernel 内部对空数据执行计算或产生其他错误。

典型表现包括:

- kernel 在执行填充或对角线提取时崩溃或报错
- 0-size Tensor 输入无法通过 `masked_fill` 或 `diag` 算子
- 梯度计算时形状处理不正确

例如:

```python
import numpy as np
import paddle

paddle.disable_static()

# masked_fill 0-size tensor 输入
x = paddle.to_tensor(np.random.rand(0, 3).astype('float32'))
mask = paddle.to_tensor(np.random.randint(0, 2, (0, 3)).astype('bool'))
value = paddle.to_tensor(np.array([1.0]).astype('float32'))
out = paddle.masked_fill(x, mask, value)
# 期望返回 shape 为 (0, 3) 的空 Tensor

# diag 0-size tensor 输入
x = paddle.to_tensor(np.random.rand(10, 0).astype('float64'))
out = paddle.diag(x, offset=1)
# 期望返回正确 shape 的空 Tensor
```

上述调用中输入包含 0-size 维度。按照 API 语义,0-size Tensor 的操作应正常返回正确 shape 的空 Tensor。

需要在 CPU/GPU/XPU kernel 层添加 0-size 早期返回处理,并修复梯度 kernel 中的形状处理逻辑。

## 验收说明

- 当输入为 0-size Tensor 时,`paddle.masked_fill` 和 `paddle.diag` 应正常完成,返回正确 shape 的空 Tensor
- 输出的 shape 应与输入一致
- 非 0-size Tensor 输入下的 masked_fill/diag 行为不得退化
- 梯度计算也应正常工作(0-size Tensor 的梯度也为空 Tensor,或保持正确形状)

## 技术要求

- 熟悉 C++ 和 Paddle PHI kernel 开发
- 了解 Tensor shape、0-size Tensor 和 kernel 执行路径
- 了解 masked_fill 和 diag 算子的输入输出语义
- 了解 Paddle CPU/GPU/XPU kernel 的实现模式
- 需要从源码编译 Paddle 以验证修改
59 changes: 59 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73570/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SWE-Paddle Task Proposal: PaddlePaddle__Paddle-73570

## 1. 来源信息

- Instance ID: `PaddlePaddle__Paddle-73570`
- PR 链接: https://github.com/PaddlePaddle/Paddle/pull/73570
- PR 标题: `[0-size Tensor Job2 No.87] Add 0-size Tensor support for masked_fill`
- Base commit: `3efb8dbb51547f0235a402135c54ed83c2f12d61`
- Gold commit: `70574f3ff130128d7cfed5a7bc50f2842137cc98`
- Merged at: 2025-07-01
- 你的身份: contributor

## 2. 问题一句话

`paddle.masked_fill` 和 `paddle.diag` 在输入为 0-size Tensor 时,CPU/GPU/XPU kernel 未处理 0-size 边界情况导致崩溃或报错,需要在 kernel 入口添加 0-size 早期返回逻辑,并修复梯度 kernel 中的形状处理。

## 3. 为什么适合作为 SWE-Paddle 样本

- **真实性**: 来自 Paddle「0-size Tensor 机制建设」系列任务,是真实研发需求。
- **代表性**: 覆盖 C++ kernel 层面的 0-size Tensor 边界处理,涉及 CPU/GPU/XPU 三端 kernel、梯度 kernel 和 InferMeta。
- **边界清楚**: 目标仅限输入为 0-size 时的 kernel 早期返回和梯度形状处理;正向非零尺寸输入不应受影响。
- **非平凡性**: 修复需要在多个 kernel 中添加 `numel() == 0` 的早期返回,并修复梯度 kernel 中使用 `phi::Full` 填充 0 以保持正确形状,涉及对 kernel 执行流程和梯度计算的理解。
- **回归护栏明确**: 目标 F2P 可覆盖 0-size Tensor 输入的 `masked_fill` 和 `diag` 算子测试;同文件中已有的标准测试用例可作为 P2P 护栏。

## 4. 任务类型和标签

- 任务类型: `bug_fix`
- 执行后端: `cpu`
- 设备范围: `cpu_only`
- 模块标签: `[operator_kernel, masked_fill, diag, 0-size_tensor, cpu_kernel, gpu_kernel, xpu_kernel]`

## 5. 验证思路

- 目标测试命令: `bash tests/test.sh`
- 目标测试文件:
- `test/legacy_test/test_diag_v2.py`(`TestDiagV2Op_ZeroSize`)
- `test/legacy_test/test_masked_fill.py`(`TestMaskedFillAPI_ZeroSize2`)
- P2P 候选: 同文件中已有的 `TestDiagV2Op`、`TestMaskedFillAPI` 等标准算子测试用例。
- 修复前预期: `base_commit` + `tests/test.patch` 后,0-size Tensor 输入的算子测试失败(kernel 崩溃或报错)。
- 修复后预期: 继续应用 `solution/code.patch` 并重新编译后,0-size Tensor 输入正常返回空 Tensor,P2P 存量测试仍然通过。

## 6. 环境与资源

- 是否能提供 Docker: 无
- Dockerfile 或镜像地址: 暂无
- Paddle 来源: `PaddlePaddle/Paddle` source checkout at `base_commit`,需要源码编译。
- OS / Python / CUDA / cuDNN / 其他关键依赖: Linux CPU + Python + numpy;编译需要 CMake、GCC;不要求 CUDA/cuDNN(CPU 编译即可验证)。
- 硬件: CPU 即可(编译和测试均不需要 GPU)。
- patch 类型: 含 C++ kernel 修改(CPU/GPU/XPU 三端)+ InferMeta + 符号推导,需要重新编译 Paddle。
- 最小测试命令: `bash tests/test.sh`
- 是否有 oracle 日志: 无

## 7. 风险自查

- 泄露风险: 正式 `instruction.md` 只描述「masked_fill/diag 对 0-size Tensor 输入的行为异常」,不指出具体 `numel() == 0` 分支逻辑或具体代码位置。
- 环境风险: 中。任务涉及 C++ kernel 修改,需要源码编译 Paddle,编译时间较长。
- flaky 风险: 低。测试使用固定的 0-size Tensor 构造,不依赖随机数差异或多设备同步。
- 拆分风险: 低。该 PR 目标集中在 `masked_fill` 和 `diag` 的 CPU/GPU/XPU kernel 0-size 早期返回和梯度形状处理,测试明确指向新增的 ZeroSize 测试类,适合作为一个独立样本。
- 其他不确定点: 完整任务包阶段应确认新增 F2P 在 `base_commit` 编译后确实失败。注意 `masked_fill` 的梯度处理需要保持正确形状(x_grad 可能非 0-size)。
176 changes: 176 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-73570/solution/code.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
diff --git a/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/unary_infer_sym.cc b/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/unary_infer_sym.cc
index 0a3ca9505ef93..fca5f88da5e26 100644
--- a/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/unary_infer_sym.cc
+++ b/paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/unary_infer_sym.cc
@@ -899,6 +899,9 @@ bool DiagOpInferSymbolicShape(pir::Operation *op,
size_ = x_shape[1].dyn_cast<int64_t>();
}
}
+ if (size_ < 0) {
+ size_ = 0;
+ }
infer_context->SetShapeOrDataForValue(
op->result(0), symbol::TensorShapeOrDataDimExprs({size_}));
} else {
diff --git a/paddle/phi/infermeta/unary.cc b/paddle/phi/infermeta/unary.cc
index 161e9b57b84d4..7fdc1bca049c3 100644
--- a/paddle/phi/infermeta/unary.cc
+++ b/paddle/phi/infermeta/unary.cc
@@ -955,6 +955,9 @@ void DiagInferMeta(const MetaTensor& x,
size_ = x_dims[1];
}
}
+ if (size_ < 0) {
+ size_ = 0;
+ }
out->set_dims({size_});
out->set_dtype(x.dtype());
} else {
diff --git a/paddle/phi/kernels/cpu/diag_grad_kernel.cc b/paddle/phi/kernels/cpu/diag_grad_kernel.cc
index b53582604b5b6..01205d2dd9117 100644
--- a/paddle/phi/kernels/cpu/diag_grad_kernel.cc
+++ b/paddle/phi/kernels/cpu/diag_grad_kernel.cc
@@ -28,6 +28,7 @@ void DiagGradKernel(const Context& dev_ctx,
int offset,
DenseTensor* x_grad) {
T* dx_data = dev_ctx.template Alloc<T>(x_grad);
+ if (x_grad && x_grad->numel() == 0) return;
const T* dout_data = out_grad.data<T>();
auto dx_dims = x_grad->dims();
auto dout_dims = out_grad.dims();
diff --git a/paddle/phi/kernels/cpu/diag_kernel.cc b/paddle/phi/kernels/cpu/diag_kernel.cc
index 5f21a4b26d307..675763fbe8f72 100644
--- a/paddle/phi/kernels/cpu/diag_kernel.cc
+++ b/paddle/phi/kernels/cpu/diag_kernel.cc
@@ -30,6 +30,7 @@ void DiagKernel(const Context& dev_ctx,
auto* x_data = x.data<T>();
auto x_dims = x.dims();
T* out_data = dev_ctx.template Alloc<T>(out);
+ if (out && out->numel() == 0) return;
auto out_dims = out->dims();

int64_t i = 0;
diff --git a/paddle/phi/kernels/cpu/masked_fill_grad_kernel.cc b/paddle/phi/kernels/cpu/masked_fill_grad_kernel.cc
index 5824e327ea569..73a0cb3e2a740 100644
--- a/paddle/phi/kernels/cpu/masked_fill_grad_kernel.cc
+++ b/paddle/phi/kernels/cpu/masked_fill_grad_kernel.cc
@@ -20,9 +20,9 @@
#include "paddle/phi/kernels/empty_kernel.h"
#include "paddle/phi/kernels/expand_grad_kernel.h"
#include "paddle/phi/kernels/expand_kernel.h"
+#include "paddle/phi/kernels/full_kernel.h"
#include "paddle/phi/kernels/funcs/common_infer_shape_functions.h"
#include "paddle/phi/kernels/funcs/common_shape.h"
-
namespace phi {

template <typename T, typename Context>
@@ -33,6 +33,19 @@ void MaskedFillGradKernel(const Context& dev_ctx,
const DenseTensor& out_grad,
DenseTensor* x_grad,
DenseTensor* v_grad) {
+ if (out_grad.numel() == 0 || mask.numel() == 0) {
+ // x shape [2, 1, 3], mask shape [2, 0, 3], x_grad shape [2, 1, 3]
+ if (x_grad) {
+ phi::Full<T, Context>(
+ dev_ctx, phi::IntArray(common::vectorize(x_grad->dims())), 0, x_grad);
+ }
+ if (v_grad) {
+ phi::Full<T, Context>(
+ dev_ctx, phi::IntArray(common::vectorize(v_grad->dims())), 0, v_grad);
+ }
+ return;
+ }
+
auto x_grad_dims = x_grad->dims();
auto mask_dims = mask.dims();
bool expand_x = false;
diff --git a/paddle/phi/kernels/cpu/masked_fill_kernel.cc b/paddle/phi/kernels/cpu/masked_fill_kernel.cc
index ac3949ef4de6c..7edace7f8ebec 100644
--- a/paddle/phi/kernels/cpu/masked_fill_kernel.cc
+++ b/paddle/phi/kernels/cpu/masked_fill_kernel.cc
@@ -29,6 +29,11 @@ void MaskedFillKernel(const Context& dev_ctx,
const DenseTensor& mask,
const DenseTensor& value,
DenseTensor* out) {
+ if (x.numel() == 0 || mask.numel() == 0) {
+ dev_ctx.template Alloc<T>(out);
+ return;
+ }
+
auto x_dims = x.dims();
auto mask_dims = mask.dims();

diff --git a/paddle/phi/kernels/gpu/diag_grad_kernel.cu b/paddle/phi/kernels/gpu/diag_grad_kernel.cu
index a93990993bc7a..13cd7cc08ae60 100644
--- a/paddle/phi/kernels/gpu/diag_grad_kernel.cu
+++ b/paddle/phi/kernels/gpu/diag_grad_kernel.cu
@@ -57,6 +57,7 @@ void DiagGradKernel(const Context& dev_ctx,
int offset,
DenseTensor* x_grad) {
T* dx_data = dev_ctx.template Alloc<T>(x_grad);
+ if (x_grad && x_grad->numel() == 0) return;
auto* dout_data = out_grad.data<T>();
auto dx_dims = x_grad->dims();
auto dout_dims = out_grad.dims();
diff --git a/paddle/phi/kernels/gpu/diag_kernel.cu b/paddle/phi/kernels/gpu/diag_kernel.cu
index 3d10b2aef24c7..036431c3ae3a3 100644
--- a/paddle/phi/kernels/gpu/diag_kernel.cu
+++ b/paddle/phi/kernels/gpu/diag_kernel.cu
@@ -63,6 +63,7 @@ void DiagKernel(const Context& dev_ctx,
auto* x_data = x.data<T>();
auto x_dims = x.dims();
T* out_data = dev_ctx.template Alloc<T>(out);
+ if (out && out->numel() == 0) return;
auto out_dims = out->dims();

auto GetBlockGridSize = [&dev_ctx](int64_t size) {
diff --git a/paddle/phi/kernels/gpu/masked_fill_grad_kernel.cu b/paddle/phi/kernels/gpu/masked_fill_grad_kernel.cu
index 60bb8b00d5b44..638032595b328 100644
--- a/paddle/phi/kernels/gpu/masked_fill_grad_kernel.cu
+++ b/paddle/phi/kernels/gpu/masked_fill_grad_kernel.cu
@@ -303,13 +303,14 @@ void MaskedFillGradKernel(const Context& dev_ctx,
DenseTensor* x_grad,
DenseTensor* v_grad) {
if (out_grad.numel() == 0 || mask.numel() == 0) {
- if (x_grad != nullptr) {
- x_grad->Resize({0});
- dev_ctx.template Alloc<T>(x_grad);
+ // x shape [2, 1, 3], mask shape [2, 0, 3], x_grad shape [2, 1, 3]
+ if (x_grad) {
+ phi::Full<T, Context>(
+ dev_ctx, phi::IntArray(common::vectorize(x_grad->dims())), 0, x_grad);
}
- if (v_grad != nullptr) {
- v_grad->Resize({0});
- dev_ctx.template Alloc<T>(v_grad);
+ if (v_grad) {
+ phi::Full<T, Context>(
+ dev_ctx, phi::IntArray(common::vectorize(v_grad->dims())), 0, v_grad);
}
return;
}
diff --git a/paddle/phi/kernels/gpu/masked_fill_kernel.cu b/paddle/phi/kernels/gpu/masked_fill_kernel.cu
index 28d5e55557bf5..0261cb316c4ab 100644
--- a/paddle/phi/kernels/gpu/masked_fill_kernel.cu
+++ b/paddle/phi/kernels/gpu/masked_fill_kernel.cu
@@ -209,7 +209,6 @@ void MaskedFillKernel(const Context& dev_ctx,
const DenseTensor& value,
DenseTensor* out) {
if (x.numel() == 0 || mask.numel() == 0) {
- out->Resize({0});
dev_ctx.template Alloc<T>(out);
return;
}
diff --git a/paddle/phi/kernels/xpu/diag_kernel.cc b/paddle/phi/kernels/xpu/diag_kernel.cc
index 8fb84f6a04aab..ad22c19bd7a2f 100644
--- a/paddle/phi/kernels/xpu/diag_kernel.cc
+++ b/paddle/phi/kernels/xpu/diag_kernel.cc
@@ -29,6 +29,7 @@ void DiagKernel(const Context& dev_ctx,
using XPUType = typename XPUTypeTrait<T>::Type;
auto* x_data = reinterpret_cast<const XPUType*>(x.data<T>());
dev_ctx.template Alloc<T>(out);
+ if (out && out->numel() == 0) return;
auto* out_data = reinterpret_cast<XPUType*>(out->data<T>());

auto x_shape = common::vectorize<int64_t>(x.dims());
Loading