}
 
        s.assignReg(r, v, c)
+       if c.Op == OpLoadReg && s.isGReg(r) {
+               s.f.Fatalf("allocValToReg.OpLoadReg targeting g: " + c.LongString())
+       }
        if nospill {
                s.nospill |= regMask(1) << r
        }
        return opcodeTable[op].reg
 }
 
+func (s *regAllocState) isGReg(r register) bool {
+       return s.f.Config.hasGReg && s.GReg == r
+}
+
 func (s *regAllocState) regalloc(f *Func) {
        regValLiveSet := f.newSparseSet(f.NumValues()) // set of values that may be live in register
        defer f.retSparseSet(regValLiveSet)
                        // Majority vote? Deepest nesting level?
                        phiRegs = phiRegs[:0]
                        var phiUsed regMask
+
                        for _, v := range phis {
                                if !s.values[v.ID].needReg {
                                        phiRegs = append(phiRegs, noRegister)
                // predecessor of it, find live values that we use soon after
                // the merge point and promote them to registers now.
                if len(b.Succs) == 1 {
+                       if s.f.Config.hasGReg && s.regs[s.GReg].v != nil {
+                               s.freeReg(s.GReg) // Spill value in G register before any merge.
+                       }
                        // For this to be worthwhile, the loop must have no calls in it.
                        top := b.Succs[0].b
                        loop := s.loopnest.b2l[top.ID]
                        c = e.p.NewValue1(pos, OpLoadReg, c.Type, c)
                }
                e.set(r, vid, c, false, pos)
+               if c.Op == OpLoadReg && e.s.isGReg(register(r.(*Register).num)) {
+                       e.s.f.Fatalf("process.OpLoadReg targeting g: " + c.LongString())
+               }
        }
 }
 
                }
        }
        e.set(loc, vid, x, true, pos)
+       if x.Op == OpLoadReg && e.s.isGReg(register(loc.(*Register).num)) {
+               e.s.f.Fatalf("processDest.OpLoadReg targeting g: " + x.LongString())
+       }
        if splice != nil {
                (*splice).Uses--
                *splice = x
 
        checkFunc(f.f)
 }
 
+// Test to make sure G register is never reloaded from spill (spill of G is okay)
+// See #25504
+func TestNoGetgLoadReg(t *testing.T) {
+       /*
+               Original:
+               func fff3(i int) *g {
+                       gee := getg()
+                       if i == 0 {
+                               fff()
+                       }
+                       return gee // here
+               }
+       */
+       c := testConfigARM64(t)
+       f := c.Fun("b1",
+               Bloc("b1",
+                       Valu("v1", OpInitMem, types.TypeMem, 0, nil),
+                       Valu("v6", OpArg, c.config.Types.Int64, 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Int64)),
+                       Valu("v8", OpGetG, c.config.Types.Int64.PtrTo(), 0, nil, "v1"),
+                       Valu("v11", OpARM64CMPconst, types.TypeFlags, 0, nil, "v6"),
+                       Eq("v11", "b2", "b4"),
+               ),
+               Bloc("b4",
+                       Goto("b3"),
+               ),
+               Bloc("b3",
+                       Valu("v14", OpPhi, types.TypeMem, 0, nil, "v1", "v12"),
+                       Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
+                       Valu("v16", OpARM64MOVDstore, types.TypeMem, 0, nil, "v8", "sb", "v14"),
+                       Exit("v16"),
+               ),
+               Bloc("b2",
+                       Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, nil, "v1"),
+                       Goto("b3"),
+               ),
+       )
+       regalloc(f.f)
+       checkFunc(f.f)
+       // Double-check that we never restore to the G register. Regalloc should catch it, but check again anyway.
+       r := f.f.RegAlloc
+       for _, b := range f.blocks {
+               for _, v := range b.Values {
+                       if v.Op == OpLoadReg && r[v.ID].String() == "g" {
+                               t.Errorf("Saw OpLoadReg targeting g register: %s", v.LongString())
+                       }
+               }
+       }
+}
+
 // Test to make sure we don't push spills into loops.
 // See issue #19595.
 func TestSpillWithLoop(t *testing.T) {