Observed issues › hecbench/gcc-c-swar-popcount-assembler-error
GCC: invalid assembly for a bit-counting loop on MI250X
Legality: independently confirmed. 2 independent readings find the program legal and the result required. How this is checked
GCC 16.2 for gfx90a emits a v_bcnt_u32_b32 instruction with a missing operand for the parallel-sum popcount loop in a target region, so the offload link fails; shift-and-add and the builtin compile.
Affected variants
| Compiler | MI300A gfx942 unified memory (XNACK 1) | MI300A gfx942 copy (XNACK 0) | MI250X gfx90a copy (XNACK 0) | MI300X gfx942 copy (XNACK 0) | MI350X gfx950 copy (XNACK 0) | A100 sm_80 separate memory | A100 sm_80 managed memory |
|---|---|---|---|---|---|---|---|
| AFAR 24.3.0 | correct | correct | unknown not run | correct | correct | n/a | n/a |
| CCE 19.0.0 | correct | correct | unknown not run | unknown not run | unknown not run | n/a | n/a |
| CCE 20.0.2 | correct | correct | unknown not run | unknown not run | unknown not run | n/a | n/a |
| CCE 21.0.3 | correct | correct | unknown not run | unknown not run | unknown not run | n/a | n/a |
| CCE 21.0.4 | correct | correct | unknown not run | unknown not run | unknown not run | n/a | n/a |
| NVHPC 25.11 | n/a | n/a | n/a | n/a | n/a | correct | correct |
| NVHPC 26.9 | n/a | n/a | n/a | n/a | n/a | correct | correct |
| GCC 16.2.0 | behaves incorrectly | behaves incorrectly | behaves incorrectly | behaves incorrectly | behaves incorrectly | correct | n/a |
"behaves incorrectly" / "not supported": this issue, confirmed in up to four environments (another build directory, another node, a repeat). "fails another way": a different failure, not counted as this issue. "unknown": not run there, or the runs disagree.
Messages
| Result | Compiler and GPU | Message |
|---|---|---|
| behaves incorrectly | GCC 16.2.0, MI300A unified GCC 16.2.0, MI300A copy GCC 16.2.0, MI300X GCC 16.2.0, MI350X | show<dir>:129:2: error: too few operands for instruction
v_bcnt_u32_b32 v0, v0
^
<dir>:145:2: error: too few operands for instruction
v_bcnt_u32_b32 v3, v3
^
<dir>:181:2: error: too few operands for instruction
v_bcnt_u32_b32 v0, v0 |
| behaves incorrectly | GCC 16.2.0, MI250X | show<dir>:126:2: error: too few operands for instruction
v_bcnt_u32_b32 v0, v0
^
<dir>:139:2: error: too few operands for instruction
v_bcnt_u32_b32 v3, v3
^
<dir>:172:2: error: too few operands for instruction
v_bcnt_u32_b32 v0, v0 |
Reproduce
GCC 16.2.0, MI300A unified
export LD_LIBRARY_PATH=$GCC_16_2/lib64:/opt/rocm-7.2.1/lib $GCC_16_2/bin/gcc -fopenmp -foffload=amdgcn-amdhsa -foffload-options=amdgcn-amdhsa=-march=gfx942 -O2 repro.c -o repro HSA_XNACK=1 OMP_TARGET_OFFLOAD=mandatory ./repro
AFAR 24.3.0, MI300A unified (runs correctly)
$AFAR/bin/amdclang -fopenmp --offload-arch=gfx942 -O2 repro.c -o repro HSA_XNACK=1 OMP_TARGET_OFFLOAD=mandatory ./repro
Program (73 lines)
// # Finding (hecbench): GCC (gfx90a): a bit-counting loop in a target region makes the generated assembly invalid
// (v_bcnt_u32_b32)
//
// - toolchains and modes that fail: gcc_g90 (GCC 16.2.0); modes: copy; -O2
// - build: gcc -fopenmp -foffload=amdgcn-amdhsa -foffload-options=amdgcn-amdhsa=-march=gfx90a -O2
// - failure: ice
// - observed: `<tmpdir>/ccRlCeSn.mkoffload.2.s:126:2: error: too few operands for instruction`; identical signature on 1 of 1
// failing cells
// - expected: compiles and prints `bad= 0` with `check=pass`
// - rule: "The functionality added to the target directive is the inclusion of an executable region to be executed on a device"
// (5.2 §13.8 p.284 l.12-13): the region is a loop of integer shifts, masks and one multiplication (the parallel-sum popcount
// idiom); the specification limits no integer operation in a target region, and with the last step written as shifts and adds,
// or as __builtin_popcountll, the compile succeeds
// - same-compiler control: controls/pc-ctl-shift-sum.c (one change from this file; correct on gcc_g90 (GCC 16.2.0); rows
// results/hecbench/red/tio_g90.jsonl, results/hecbench/red/tuo.jsonl, results/hecbench/red/wt1.jsonl);
// controls/pc-ctl-builtin-popcount.c (one change from this file; correct on gcc_g90 (GCC 16.2.0); rows
// results/hecbench/red/tio_g90.jsonl, results/hecbench/red/tuo.jsonl, results/hecbench/red/wt1.jsonl)
// - other families: afar (AFAR 24.3.0), cce19 (CCE 19.0.0), cce20 (CCE 20.0.2), cce21 (CCE 21.0.3), cce21_4 (CCE 21.0.4), nvhpc
// (NVHPC 25.11), nvhpc269 (NVHPC 26.9), gcc_sm80 (GCC 16.2.0) run the file correctly (rows results/hecbench/red/tio_g90.jsonl,
// results/hecbench/red/tuo.jsonl, results/hecbench/red/wt1.jsonl)
// - gate and confirmer: campaign gate of the programs below: 1 cells, 1 confirmed in the four environments (a-d; (c) on another
// node or GPU; rows results/hecbench/dev/gate_reruns.part.<host>.2090963.jsonl,
// results/hecbench/dev/gate_reruns.part.<host>.2185748.jsonl); 1 of 1 cells reproduce in all four environments (a-d; (c) on
// another node or GPU than (a); rows results/hecbench/red/tio_g90.jsonl, results/hecbench/red/tio_gate_abd.jsonl,
// results/hecbench/red/tio_gate_c.jsonl)
// - origin: HeCBench `src/popcount-omp/main.cpp` (https://github.com/ORNL/HeCBench, commit
// 8f44ff3e69fa003f870963045988190a275534cb), whose gcc_g90 cell fails at the link with `error: too few operands for
// instruction` in mkoffload's assembly. This file is a small program written for this finding and reduced with tools/reduce.py;
// the bit-counting steps are the textbook parallel-sum popcount, and it holds no benchmark source.
// - programs (1): hecbench-popcount-omp
// - legality: offload.engine.prerun.check (the rules, and AFAR's front end on Tuolumne, 2026-11-06: two witnesses) accepts this file
// and the controls; AFAR, CCE and NVHPC compile and run it correctly (front ends of three other families). GCC is a compiler
// under test only and never a legality witness.
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#define M1 0x5555555555555555UL
#define M2 0x3333333333333333UL
#define M4 0x0f0f0f0f0f0f0f0fUL
#define H01 0x0101010101010101UL
int main(void)
{
const int length = 1024;
int hf = 1, bad = 0;
unsigned long *data = malloc(length * sizeof(unsigned long));
int *result = malloc(length * sizeof(int));
for (int i = 0; i < length; i++)
data[i] = (unsigned long)(i + 1) * 0x9E3779B97F4A7C15UL;
#pragma omp target map(from: hf)
hf = omp_is_initial_device() ? 1 : 0;
#pragma omp target map(to: data[0:length]) map(from: result[0:length])
for (int i = 0; i < length; i++) {
unsigned long x = data[i];
x -= (x >> 1) & M1;
x = (x & M2) + ((x >> 2) & M2);
x = (x + (x >> 4)) & M4;
result[i] = (x * H01) >> 56;
}
for (int i = 0; i < length; i++) {
int count = 0;
for (unsigned long y = data[i]; y; count++) y &= y - 1;
if (count != result[i]) bad++;
}
printf("bad= %d\n", bad);
printf("RESULT ran_on_host=%d check=%s\n", hf, bad == 0 ? "pass" : "fail");
free(data);
free(result);
return 0;
}
Something to add? Dispute its legality · Report it fixed in a newer release · Report a mistake on this page
Expected and observed
Observed where it behaves incorrectly
<dir>:129:2: error: too few operands for instruction
v_bcnt_u32_b32 v0, v0
^
<dir>:145:2: error: too few operands for instruction
v_bcnt_u32_b32 v3, v3
^
<dir>:181:2: error: too few operands for instruction
v_bcnt_u32_b32 v0, v0What the specification requires
The specification text this program relies on and the compiler is expected to support.
OpenMP 5.2 §13.8 p.284 l.12-13
The functionality added to the target directive is the inclusion of an executable region to be executed on a device
Evidence
Same compiler, one change
- one change from the finding; correct on gcc_g90: AFAR 24.3.0: correct; CCE 19.0.0: correct; CCE 20.0.2: correct; CCE 21.0.3: correct; CCE 21.0.4: correct; NVHPC 25.11: correct; NVHPC 26.9: correct; GCC 16.2.0: correct
Control program
#include <stdio.h> #include <stdlib.h> #include <omp.h> #define M1 0x5555555555555555UL #define M2 0x3333333333333333UL #define M4 0x0f0f0f0f0f0f0f0fUL #define H01 0x0101010101010101UL int main(void) { const int length = 1024; int hf = 1, bad = 0; unsigned long *data = malloc(length * sizeof(unsigned long)); int *result = malloc(length * sizeof(int)); for (int i = 0; i < length; i++) data[i] = (unsigned long)(i + 1) * 0x9E3779B97F4A7C15UL; #pragma omp target map(from: hf) hf = omp_is_initial_device() ? 1 : 0; #pragma omp target map(to: data[0:length]) map(from: result[0:length]) for (int i = 0; i < length; i++) { unsigned long x = data[i]; x -= (x >> 1) & M1; x = (x & M2) + ((x >> 2) & M2); x = (x + (x >> 4)) & M4; x += x >> 8; x += x >> 16; x += x >> 32; result[i] = x & 0x7f; } for (int i = 0; i < length; i++) { int count = 0; for (unsigned long y = data[i]; y; count++) y &= y - 1; if (count != result[i]) bad++; } printf("bad= %d\n", bad); printf("RESULT ran_on_host=%d check=%s\n", hf, bad == 0 ? "pass" : "fail"); free(data); free(result); return 0; } - one change from the finding; correct on gcc_g90: AFAR 24.3.0: correct; CCE 19.0.0: correct; CCE 20.0.2: correct; CCE 21.0.3: correct; CCE 21.0.4: correct; NVHPC 25.11: correct; NVHPC 26.9: correct; GCC 16.2.0: correct
Control program
#include <stdio.h> #include <stdlib.h> #include <omp.h> #define M1 0x5555555555555555UL #define M2 0x3333333333333333UL #define M4 0x0f0f0f0f0f0f0f0fUL #define H01 0x0101010101010101UL int main(void) { const int length = 1024; int hf = 1, bad = 0; unsigned long *data = malloc(length * sizeof(unsigned long)); int *result = malloc(length * sizeof(int)); for (int i = 0; i < length; i++) data[i] = (unsigned long)(i + 1) * 0x9E3779B97F4A7C15UL; #pragma omp target map(from: hf) hf = omp_is_initial_device() ? 1 : 0; #pragma omp target map(to: data[0:length]) map(from: result[0:length]) for (int i = 0; i < length; i++) { unsigned long x = data[i]; result[i] = __builtin_popcountll(x); } for (int i = 0; i < length; i++) { int count = 0; for (unsigned long y = data[i]; y; count++) y &= y - 1; if (count != result[i]) bad++; } printf("bad= %d\n", bad); printf("RESULT ran_on_host=%d check=%s\n", hf, bad == 0 ? "pass" : "fail"); free(data); free(result); return 0; }
Other compilers run it correctly
AFAR 24.3.0, CCE 19.0.0, CCE 20.0.2, CCE 21.0.3, CCE 21.0.4, NVHPC 25.11, NVHPC 26.9