Potential bug in gcc for K3 A100

Hello,

We have found a potential issue with the gcc toolchain and have provided a simple example .c file which compiles and executes fine on a K3 X100 core but results in stack smashing on an A100 core. The minimal example .c file is:

#define _GNU_SOURCE
#include <riscv_vector.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sched.h>
#include <unistd.h>
typedef struct { int16_t r, i; } c16_t;
static int32_t chlevel_ref(const c16_t *ch,int len,int shift,int16_t scale){
  int64_t avg=0; for(int i=0;i<(len>>2);i++) for(int k=0;k<4;k++){const c16_t c=ch[i*4+k]; avg+=((int32_t)c.r*c.r+(int32_t)c.i*c.i)>>shift;}
  return (int32_t)(uint32_t)(avg/scale);
}
static void scale_ref(c16_t *ch,int len,int shift){ for(int i=0;i<len;i++){ch[i].r>>=shift;ch[i].i>>=shift;} }
static int32_t chlevel_rvv(const c16_t *ch,int len,int shift,int16_t scale){
  const int16_t *p=(const int16_t*)ch; int64_t acc=0; int nc=len&~3;
  for(int n=0;n<nc;){ size_t vl=__riscv_vsetvl_e16m1(nc-n);
    vint16m1x2_t v=__riscv_vlseg2e16_v_i16m1x2(p+2*n,vl);
    vint16m1_t cr=__riscv_vget_v_i16m1x2_i16m1(v,0),ci=__riscv_vget_v_i16m1x2_i16m1(v,1);
    vint32m2_t pw=__riscv_vwmacc_vv_i32m2(__riscv_vwmul_vv_i32m2(cr,cr,vl),ci,ci,vl);
    pw=__riscv_vsra_vx_i32m2(pw,shift,vl);
    acc+=__riscv_vmv_x_s_i64m1_i64(__riscv_vwredsum_vs_i32m2_i64m1(pw,__riscv_vmv_s_x_i64m1(0,1),vl)); n+=(int)vl; }
  return (int32_t)(uint32_t)(acc/scale);
}
static void scale_rvv(c16_t *ch,int len,int shift){ int16_t *p=(int16_t*)ch;
  for(int n=0;n<2*len;){ size_t vl=__riscv_vsetvl_e16m1(2*len-n);
    __riscv_vse16_v_i16m1(p+n,__riscv_vsra_vx_i16m1(__riscv_vle16_v_i16m1(p+n,vl),shift,vl),vl); n+=(int)vl; } }
static uint32_t rng=0xC0FFEE; static int16_t rnd16(void){rng=rng*1103515245u+12345u;return (int16_t)(rng>>16);}
int main(int argc,char**argv){
  if(argc>1){int c=atoi(argv[1]);if(c>7){FILE*f=fopen("/proc/set_ai_thread","w");if(f){fprintf(f,"%ld\n",(long)getpid());fclose(f);}}cpu_set_t s;CPU_ZERO(&s);CPU_SET(c,&s);sched_setaffinity(0,sizeof(s),&s);}
  setbuf(stdout,NULL); printf("VLEN=%zu\n",(size_t)__riscv_vlenb()*8);
  int lens[]={1272,4,12,100};
  for(unsigned t=0;t<4;t++){int len=lens[t];
    c16_t *ch=malloc(sizeof(c16_t)*len),*tmp=malloc(sizeof(c16_t)*len),*tmp2=malloc(sizeof(c16_t)*len);
    for(int i=0;i<len;i++){ch[i].r=rnd16();ch[i].i=rnd16();}
    for(int sh=0;sh<=4;sh+=2){int16_t sc=(len>>1)|1;
      int32_t lr=chlevel_ref(ch,len,sh,sc),lg=chlevel_rvv(ch,len,sh,sc);
      for(int i=0;i<len;i++){tmp[i]=ch[i];tmp2[i]=ch[i];}
      scale_ref(tmp,len,sh);scale_rvv(tmp2,len,sh);
      int sd=0;for(int i=0;i<len;i++)if(tmp[i].r!=tmp2[i].r||tmp[i].i!=tmp2[i].i)sd++;
      printf("len=%d sh=%d level %d/%d scale d=%d\n",len,sh,lr,lg,sd);
    }
    free(ch);free(tmp);free(tmp2);
  }
  printf("done\n"); return 0;
}

The complete description is:

spacemit-gcc 15.2.0: -O2 RVV codegen smashes the stack at VLEN=1024

Compiler: riscv64-unknown-linux-gnu-gcc (gf3b8c022145) 15.2.0 (SpaceMIT toolchain)
Target: SpaceMIT K3, the 1024-bit vector cores (VLEN=1024). Fine on the 256-bit cores.
Flags: -march=rv64gcv_zba_zbb_zbc_zbs_zicond -mabi=lp64d -O2

Symptom

A function that mixes hand-written RVV intrinsics (segment loads, widening
multiply/reduction) with ordinary scalar loops computes entirely correct
results
, then SIGSEGV on return — the saved return address is overwritten:

Program received signal SIGSEGV
ra   0x2   0x2
pc   0x2   0x2      <- returned into garbage; the stack slot holding ra was clobbered

Reproduces / does not

build core result
-O2 VLEN=1024 (cpu8) SIGSEGV (ra/pc = 0x2)
-O0 VLEN=1024 (cpu8) clean
-O2 VLEN=256 (cpu2) clean
-O2 -fno-tree-vectorize VLEN=1024 still SIGSEGV
-O2 -fno-tree-slp-vectorize VLEN=1024 still SIGSEGV

So it is not the auto-vectorizer — it is -O2 register-allocation / frame
sizing for the hand-written intrinsics, and only at VLEN=1024.

Root-cause evidence (disassembly of the crashing function)

-O2 prologue + the vector spills:

addi sp,sp,-272        ; callee-saved area; ra saved at sp+264
csrr t0,vlenb          ; t0 = VLEN/8 = 128 at VLEN=1024
sub  sp,sp,t0          ; reserve ONE vlenb (=128B) vector spill slot
add  s2,sp, vlenb+32   ; a spill pointer that lands ABOVE that slot,
                       ;   i.e. inside the 272-byte callee-saved region
...
vs1r.v v6,(a5)         ; WHOLE-REGISTER spill = 128 bytes (ignores vl)
vs1r.v v6,(a1)         ; a second whole-register spill
vse32.v v1,(s2) / vse16.v v1,(...)

The routine performs several 128-byte whole-register (vs1r.v) spills, but the
prologue reserves only a single vlenb slot, and at least one spill pointer
(s2 = sp + vlenb + 32) points into the callee-saved region. At VLEN=1024 a
128-byte whole-register store therefore overruns into the saved ra slot,
corrupting the return address — matching the observed ra=0x2. At VLEN=256
vlenb=32 and the layout does not overlap; at -O0 no such spilling occurs.

Minimal reproducer

See full_repro.c (self-contained, ~55 lines). Build & run:

riscv64-unknown-linux-gnu-gcc --sysroot=$SYSROOT \
  -march=rv64gcv_zba_zbb_zbc_zbs_zicond -mabi=lp64d -O2 -g \
  full_repro.c -o full_repro
# on the board, pinned to a 1024-bit core (K3: cpu 8-15):
./full_repro 8      # -> prints correct results, then "Segmentation fault"
./full_repro 2      # -> clean (256-bit core)

The kernels are correct (rvv-vs-scalar bit-exact for every line printed); the
fault is purely the return from the routine.

Impact

Blocks otherwise-correct RVV kernels (here a channel-power reduction and a
scaling loop) whenever -O2 register pressure forces multiple vector-register
spills in a function running at VLEN=1024. -O0/-O1 per-file and
-fno-tree-vectorize do not reliably avoid it; the only robust workaround found
so far is to keep such code off the 1024-bit path, so a compiler fix is needed.

Thanks for the detailed report. The root cause is that when switching cores via the /proc/set_ai_thread interface, you must ensure no vector-related operations have been executed beforehand; otherwise, a change in vlen after the switch may lead to issues.
This cannot be resolved at the compiler level; instead, a safer approach is required to leverage the A100 cores.
You can try using aix, or k3_taskset , or alternatively switch the entire bash process to an A100 core before launching your program.

issue:

bianbu@k3-deb1-25:~$ ./full_repro 8
VLEN=1024
len=1272 sh=0 level 1463946098/1463946098 scale d=0
len=1272 sh=2 level 365986524/365986524 scale d=0
len=1272 sh=4 level 91496630/91496630 scale d=0
len=4 sh=0 level 1232272759/1232272759 scale d=0
len=4 sh=2 level 308068189/308068189 scale d=0
len=4 sh=4 level 77017046/77017046 scale d=0
len=12 sh=0 level 1566175240/1566175240 scale d=0
len=12 sh=2 level 391543809/391543809 scale d=0
len=12 sh=4 level 97885952/97885952 scale d=0
len=100 sh=0 level 1428383974/1428383974 scale d=0
len=100 sh=2 level 357095993/357095993 scale d=0
len=100 sh=4 level 89273997/89273997 scale d=0
done
段错误                  ./full_repro 8

use aix

bianbu@k3-deb1-25:~$ aix ./full_repro 8
VLEN=1024
len=1272 sh=0 level 1463946098/1463946098 scale d=0
len=1272 sh=2 level 365986524/365986524 scale d=0
len=1272 sh=4 level 91496630/91496630 scale d=0
len=4 sh=0 level 1232272759/1232272759 scale d=0
len=4 sh=2 level 308068189/308068189 scale d=0
len=4 sh=4 level 77017046/77017046 scale d=0
len=12 sh=0 level 1566175240/1566175240 scale d=0
len=12 sh=2 level 391543809/391543809 scale d=0
len=12 sh=4 level 97885952/97885952 scale d=0
len=100 sh=0 level 1428383974/1428383974 scale d=0
len=100 sh=2 level 357095993/357095993 scale d=0
len=100 sh=4 level 89273997/89273997 scale d=0
done

k3_taskset

bianbu@k3-deb1-25:~$ ~/k3_taskset/taskset -c 8 ./full_repro 8
VLEN=1024
len=1272 sh=0 level 1463946098/1463946098 scale d=0
len=1272 sh=2 level 365986524/365986524 scale d=0
len=1272 sh=4 level 91496630/91496630 scale d=0
len=4 sh=0 level 1232272759/1232272759 scale d=0
len=4 sh=2 level 308068189/308068189 scale d=0
len=4 sh=4 level 77017046/77017046 scale d=0
len=12 sh=0 level 1566175240/1566175240 scale d=0
len=12 sh=2 level 391543809/391543809 scale d=0
len=12 sh=4 level 97885952/97885952 scale d=0
len=100 sh=0 level 1428383974/1428383974 scale d=0
len=100 sh=2 level 357095993/357095993 scale d=0
len=100 sh=4 level 89273997/89273997 scale d=0
done

switch bash

bianbu@k3-deb1-25:~$ echo $$ > /proc/set_ai_thread
bianbu@k3-deb1-25:~$ ./full_repro 8
VLEN=1024
len=1272 sh=0 level 1463946098/1463946098 scale d=0
len=1272 sh=2 level 365986524/365986524 scale d=0
len=1272 sh=4 level 91496630/91496630 scale d=0
len=4 sh=0 level 1232272759/1232272759 scale d=0
len=4 sh=2 level 308068189/308068189 scale d=0
len=4 sh=4 level 77017046/77017046 scale d=0
len=12 sh=0 level 1566175240/1566175240 scale d=0
len=12 sh=2 level 391543809/391543809 scale d=0
len=12 sh=4 level 97885952/97885952 scale d=0
len=100 sh=0 level 1428383974/1428383974 scale d=0
len=100 sh=2 level 357095993/357095993 scale d=0
len=100 sh=4 level 89273997/89273997 scale d=0
done