https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126285
Bug ID: 126285
Summary: gcobol: USAGE COMP-5 COMPUTE not lowered to host
integer arithmetic
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: cobol
Assignee: unassigned at gcc dot gnu.org
Reporter: peeterjoot at protonmail dot com
Target Milestone: ---
Created attachment 65063
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65063&action=edit
comp5-compute-via-runtime.cob mentioned in the report.
# Summary
At `-O2`, `COMPUTE` / `MULTIPLY` / `ADD` / `SUBTRACT` / `DIVIDE` on
`USAGE COMP-5` (binary integer) operands are implemented entirely as calls into
`libgcobol` field-arithmetic helpers (`__gg__multiplyf2`,
`__gg__add_fixed_phase1`, `__gg__subtractf2_fixed_phase1`,
`__gg__fixed_phase2_assign_to_c`, `__gg__move`, plus per-op
`__gg__initialize_variable_clean` of intermediate stack fields and
`__gg__process_compute_error`). No host `mul` / `add` / `sdiv` appears in the
hot path.
For a tight 5 000 000-iteration COMP-5 loop this is about **250× slower** than
the equivalent C/C++ `-O2 -fno-tree-vectorize` loop (or another COBOL compiler
that lowers COMP-5 to native integers). Even the trivial statement
```cobol
COMPUTE WS-X = WS-X + 1
```
with `WS-X PIC S9(9) USAGE COMP-5` expands to
`add_fixed_phase1` → `fixed_phase2_assign_to_c` → `move` →
`process_compute_error`.
# Environment
- Compiler: `gcobol` from GCC trunk (`/opt/gcc-trunk-20260710`, 2026-07-10)
- Platform: Linux aarch64 (also expected on x86-64)
- Options: `-O2 -ffixed-form -main`
# Reproducer
`comp5-compute-via-runtime.cob` (this directory).
```
gcobol -O2 -ffixed-form -main -o slowcomp5 \
gcobol-bugs/comp5-compute-via-runtime.cob
time ./slowcomp5
objdump -d slowcomp5 --disassemble=slowcomp5 | grep -E '__gg__|mul |add '
```
Pure C++ baseline (same arithmetic, no COBOL runtime) is
`comp5-compute-via-runtime.cpp`:
```
g++ -O2 -fno-tree-vectorize -std=c++20 -o slowcomp5-cpp \
gcobol-bugs/comp5-compute-via-runtime.cpp
time ./slowcomp5-cpp
```
`-fno-tree-vectorize` keeps the loop scalar so the timing is an
apples-to-apples
compare with gcobol (whose COMP-5 `COMPUTE` path is scalar `libgcobol` calls,
not
SIMD). Plain `g++ -O2` vectorizes this loop 4-wide, which would overstate the
native-integer advantage.
Equivalent C for the timing baseline (same flag):
```c
#include <stdio.h>
#include <stdint.h>
int main(void) {
int32_t n = 5000000;
int64_t x = 0;
for (int32_t i = 1; i <= n; ++i) {
int32_t a = 2 * i - 1;
int32_t b = i * a;
x = x + b;
}
printf("%lld\n", (long long)x);
return 0;
}
```
```
gcc -O2 -fno-tree-vectorize -o slowcomp5-c slowcomp5.c
```
# Observed (gcobol -O2)
Timing (5e6 iterations, aarch64):
| build | wall time |
|---|---|
| gcobol `-O2` | ~0.76 s |
| `g++ -O2 -fno-tree-vectorize` C++ baseline | ~0.002 s |
| ratio | ~**380×** |
`objdump` of `slowcomp5`: every arithmetic step in the loop is a PLT call, e.g.
```
bl __gg__initialize_variable_clean
bl __gg__multiplyf2
bl __gg__initialize_variable_clean
bl __gg__subtractf2_fixed_phase1
bl __gg__fixed_phase2_assign_to_c
bl __gg__move
bl __gg__process_compute_error
...
bl __gg__multiplyf2
...
bl __gg__add_fixed_phase1
...
```
There is **no** host `mul` / `madd` / integer `add` for the COMPUTE bodies.
`-fdump-tree-optimized` shows the same call sequence with descriptor setup into
`cbl_field_ptr_t` arrays before each helper.
Numeric result is correct (`12237243672096`); this is a performance / codegen
issue, not a wrong-answer bug for this program.
# Expected
For `USAGE COMP-5` / `BINARY` items with no `ON SIZE ERROR`, scale (`V`), or
decimal intermediate, `-O2` should lower simple `COMPUTE` expressions to host
integer operations (as the C baseline does), rather than routing every operator
through the general field runtime.
# Additional observation (compound COMPUTE)
In a larger program (batched pi-spigot,
`CALCPI/COBOL/spigot/calcpi-spigot-batch.cob`),
compound expressions such as
```cobol
COMPUTE LS-X = LS-B * LK-A(A-IDX) + LS-CARRY
```
with all binary (`COMP-5`) operands also introduce **`float:128` intermediate
stack fields** and call `__gg__add_float_phase1` /
`__gg__float_phase2_assign_to_c`. That path is not required for the minimal
reproducer above (fixed-phase helpers suffice to show the slowdown), but it
amplifies the cost further in real numerical kernels (~30–50× vs a native
COMP-5 lowering of the same source).
# Related
Found while comparing gcobol vs a native COMP-5 lowering of a toy pi
calculation program
`CALCPI/COBOL/spigot/calcpi-spigot-batch.cob` (same algorithm, same digit
count).