commit 3715bf9b86a86ed6a3a857cabfc7dff5d70b409b Author: Russ Cox Date: Sat Mar 1 16:47:07 2025 -0500 sys/src/9: allow floating point in note handlers It's well past time to allow floating point in note handlers, since that also allows SSE and other SIMD code. mk fpnote.run in /usr/glenda/src for a test. For golang/go#62507. --- a/sys/src/9/pc/dat.h +++ b/sys/src/9/pc/dat.h @@ -57,6 +57,10 @@ struct Label /* * FPsave.status + * + * FPinit: never used + * FPactive: in use, registers are live + * FPinactive: not in use, registers are saved */ enum { @@ -64,9 +68,11 @@ enum FPinit= 0, FPactive= 1, FPinactive= 2, + FPnotestart = 3, - /* the following is a bit that can be or'd into the state */ - FPillegal= 0x100, + /* state repeated 3 bits up for note handler */ + FPnoteshift = 3, + FPnotemask = 7<<3, }; struct FPstate /* x87 fpu state */ --- a/sys/src/9/pc/main.c +++ b/sys/src/9/pc/main.c @@ -277,7 +277,7 @@ userinit(void) * Kernel Stack * * N.B. make sure there's enough space for syscall to check - * for valid args and + * for valid args and * 4 bytes for gotolabel's return PC */ p->sched.pc = (ulong)init0; @@ -640,10 +640,34 @@ mathemu(Ureg *ureg, void*) { ulong status, control; - if(up->fpstate & FPillegal){ - /* someone did floating point in a note handler */ - postnote(up, 1, "sys: floating point in note handler", NDebug); - return; + if(up->fpstate & FPnotemask){ + /* in note handler */ + switch(up->fpstate>>FPnoteshift){ + case FPnotestart: + /* no FP used yet; copy state at time of note */ + if((up->fpstate&~FPnotemask) == FPinit) { + /* no FP in the process yet */ + up->fpstate = (up->fpstate&~FPnotemask) | (FPactive<notefpsave = up->fpsave; + /* fall through */ + case FPinactive: + /* restore state */ + mathstate(&status, nil, &control); + if((status & ~control) & 0x07F) + break; + fprestore(&up->notefpsave); + up->fpstate = (up->fpstate&~FPnotemask) | (FPactive<pid, up->text, ureg->pc); + } + postnote(up, 1, "sys: floating point exception in note handler", NDebug); + return; } switch(up->fpstate){ case FPinit: @@ -723,7 +747,15 @@ procsave(Proc *p) cycles(&t); p->pcycles += t; - if(p->fpstate == FPactive){ + if(p->fpstate&FPnotemask){ + if((p->fpstate>>FPnoteshift) == FPactive){ + if(p->state == Moribund) + fpclear(); + else + fpsave(&p->notefpsave); + p->fpstate = (p->fpstate&~FPnotemask) | (FPinactive<fpstate == FPactive){ if(p->state == Moribund) fpclear(); else{ --- a/sys/src/9/pc/trap.c +++ b/sys/src/9/pc/trap.c @@ -699,9 +699,14 @@ syscall(Ureg* ureg) startns = todget(nil); } - if(scallnr == RFORK && up->fpstate == FPactive){ - fpsave(&up->fpsave); - up->fpstate = FPinactive; + if(scallnr == RFORK){ + if(up->fpstate == FPactive){ + fpsave(&up->fpsave); + up->fpstate = FPinactive; + }else if((up->fpstate>>FPnoteshift) == FPactive){ + fpsave(&up->notefpsave); + up->fpstate = (up->fpstate&~FPnotemask) | (FPinactive<fpsave); up->fpstate = FPinactive; } - up->fpstate |= FPillegal; + up->fpstate |= FPnotestart<debug); @@ -879,7 +884,9 @@ noted(Ureg* ureg, ulong arg0) nureg = up->ureg; /* pointer to user returned Ureg struct */ - up->fpstate &= ~FPillegal; + if((up->fpstate>>FPnoteshift) == FPactive) + fpoff(); + up->fpstate &= ~FPnotemask; /* sanity clause */ oureg = (ulong)nureg; --- a/sys/src/9/port/portdat.h +++ b/sys/src/9/port/portdat.h @@ -753,6 +753,7 @@ struct Proc short notified; /* sysnoted is due */ Note lastnote; int (*notify)(void*, char*); + FPsave notefpsave; Lock *lockwait; Lock *lastlock; /* debugging */ --- a/sys/src/9/port/sysproc.c +++ b/sys/src/9/port/sysproc.c @@ -181,7 +181,10 @@ sysrfork(ulong *arg) p->noteid = up->noteid; /* don't penalize the child, it hasn't done FP in a note handler. */ - p->fpstate = up->fpstate & ~FPillegal; + if((up->fpstate>>FPnoteshift) != 0){ + fpoff(); + p->fpstate &= ~FPnotemask; + } pid = p->pid; memset(p->time, 0, sizeof(p->time)); p->time[TReal] = MACHP(0)->ticks;