/* * generate: * call f * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack * proc=3 normal call to C pointer (not Go func value) */ func ginscall(f *gc.Node, proc int) { if f.Type != nil { extra := int32(0) if proc == 1 || proc == 2 { extra = 2 * int32(gc.Widthptr) } gc.Setmaxarg(f.Type, extra) } switch proc { default: gc.Fatal("ginscall: bad proc %d", proc) case 0, // normal call -1: // normal call but no return if f.Op == gc.ONAME && f.Class == gc.PFUNC { if f == gc.Deferreturn { // Deferred calls will appear to be returning to // the CALL deferreturn(SB) that we are about to emit. // However, the stack trace code will show the line // of the instruction byte before the return PC. // To avoid that being an unrelated instruction, // insert a ppc64 NOP that we will have the right line number. // The ppc64 NOP is really or r0, r0, r0; use that description // because the NOP pseudo-instruction would be removed by // the linker. var reg gc.Node gc.Nodreg(®, gc.Types[gc.TINT], ppc64.REG_R0) gins(ppc64.AOR, ®, ®) } p := gins(ppc64.ABL, nil, f) gc.Afunclit(&p.To, f) if proc == -1 || gc.Noreturn(p) { gins(obj.AUNDEF, nil, nil) } break } var reg gc.Node gc.Nodreg(®, gc.Types[gc.Tptr], ppc64.REGCTXT) var r1 gc.Node gc.Nodreg(&r1, gc.Types[gc.Tptr], ppc64.REG_R3) gmove(f, ®) reg.Op = gc.OINDREG gmove(®, &r1) reg.Op = gc.OREGISTER ginsBL(®, &r1) case 3: // normal call of c function pointer ginsBL(nil, f) case 1, // call in new proc (go) 2: // deferred call (defer) var con gc.Node gc.Nodconst(&con, gc.Types[gc.TINT64], int64(gc.Argsize(f.Type))) var reg gc.Node gc.Nodreg(®, gc.Types[gc.TINT64], ppc64.REG_R3) var reg2 gc.Node gc.Nodreg(®2, gc.Types[gc.TINT64], ppc64.REG_R4) gmove(f, ®) gmove(&con, ®2) p := gins(ppc64.AMOVW, ®2, nil) p.To.Type = obj.TYPE_MEM p.To.Reg = ppc64.REGSP p.To.Offset = 8 p = gins(ppc64.AMOVD, ®, nil) p.To.Type = obj.TYPE_MEM p.To.Reg = ppc64.REGSP p.To.Offset = 16 if proc == 1 { ginscall(gc.Newproc, 0) } else { if gc.Hasdefer == 0 { gc.Fatal("hasdefer=0 but has defer") } ginscall(gc.Deferproc, 0) } if proc == 2 { gc.Nodreg(®, gc.Types[gc.TINT64], ppc64.REG_R3) p := gins(ppc64.ACMP, ®, nil) p.To.Type = obj.TYPE_REG p.To.Reg = ppc64.REGZERO p = gc.Gbranch(ppc64.ABEQ, nil, +1) cgen_ret(nil) gc.Patch(p, gc.Pc) } } }
/* * generate: * call f * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack * proc=3 normal call to C pointer (not Go func value) */ func ginscall(f *gc.Node, proc int) { if f.Type != nil { extra := int32(0) if proc == 1 || proc == 2 { extra = 2 * int32(gc.Widthptr) } gc.Setmaxarg(f.Type, extra) } switch proc { default: gc.Fatal("ginscall: bad proc %d", proc) case 0, // normal call -1: // normal call but no return if f.Op == gc.ONAME && f.Class == gc.PFUNC { if f == gc.Deferreturn { // Deferred calls will appear to be returning to // the CALL deferreturn(SB) that we are about to emit. // However, the stack trace code will show the line // of the instruction byte before the return PC. // To avoid that being an unrelated instruction, // insert an x86 NOP that we will have the right line number. // x86 NOP 0x90 is really XCHG AX, AX; use that description // because the NOP pseudo-instruction will be removed by // the linker. var reg gc.Node gc.Nodreg(®, gc.Types[gc.TINT], x86.REG_AX) gins(x86.AXCHGL, ®, ®) } p := gins(obj.ACALL, nil, f) gc.Afunclit(&p.To, f) if proc == -1 || gc.Noreturn(p) { gins(obj.AUNDEF, nil, nil) } break } var reg gc.Node gc.Nodreg(®, gc.Types[gc.Tptr], x86.REG_DX) var r1 gc.Node gc.Nodreg(&r1, gc.Types[gc.Tptr], x86.REG_BX) gmove(f, ®) reg.Op = gc.OINDREG gmove(®, &r1) reg.Op = gc.OREGISTER gins(obj.ACALL, ®, &r1) case 3: // normal call of c function pointer gins(obj.ACALL, nil, f) case 1, // call in new proc (go) 2: // deferred call (defer) var stk gc.Node stk.Op = gc.OINDREG stk.Val.U.Reg = x86.REG_SP stk.Xoffset = 0 // size of arguments at 0(SP) var con gc.Node gc.Nodconst(&con, gc.Types[gc.TINT32], int64(gc.Argsize(f.Type))) gins(x86.AMOVL, &con, &stk) // FuncVal* at 4(SP) stk.Xoffset = int64(gc.Widthptr) gins(x86.AMOVL, f, &stk) if proc == 1 { ginscall(gc.Newproc, 0) } else { ginscall(gc.Deferproc, 0) } if proc == 2 { var reg gc.Node gc.Nodreg(®, gc.Types[gc.TINT32], x86.REG_AX) gins(x86.ATESTL, ®, ®) p := gc.Gbranch(x86.AJEQ, nil, +1) cgen_ret(nil) gc.Patch(p, gc.Pc) } } }
/* * generate: * call f * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack * proc=3 normal call to C pointer (not Go func value) */ func ginscall(f *gc.Node, proc int) { if f.Type != nil { extra := int32(0) if proc == 1 || proc == 2 { extra = 2 * int32(gc.Widthptr) } gc.Setmaxarg(f.Type, extra) } switch proc { default: gc.Fatal("ginscall: bad proc %d", proc) case 0, // normal call -1: // normal call but no return if f.Op == gc.ONAME && f.Class == gc.PFUNC { if f == gc.Deferreturn { // Deferred calls will appear to be returning to // the BL deferreturn(SB) that we are about to emit. // However, the stack trace code will show the line // of the instruction before that return PC. // To avoid that instruction being an unrelated instruction, // insert a NOP so that we will have the right line number. // ARM NOP 0x00000000 is really AND.EQ R0, R0, R0. // Use the latter form because the NOP pseudo-instruction // would be removed by the linker. var r gc.Node gc.Nodreg(&r, gc.Types[gc.TINT], arm.REG_R0) p := gins(arm.AAND, &r, &r) p.Scond = arm.C_SCOND_EQ } p := gins(arm.ABL, nil, f) gc.Afunclit(&p.To, f) if proc == -1 || gc.Noreturn(p) { gins(obj.AUNDEF, nil, nil) } break } var r gc.Node gc.Nodreg(&r, gc.Types[gc.Tptr], arm.REG_R7) var r1 gc.Node gc.Nodreg(&r1, gc.Types[gc.Tptr], arm.REG_R1) gmove(f, &r) r.Op = gc.OINDREG gmove(&r, &r1) r.Op = gc.OREGISTER r1.Op = gc.OINDREG gins(arm.ABL, &r, &r1) case 3: // normal call of c function pointer gins(arm.ABL, nil, f) case 1, // call in new proc (go) 2: // deferred call (defer) var r gc.Node regalloc(&r, gc.Types[gc.Tptr], nil) var con gc.Node gc.Nodconst(&con, gc.Types[gc.TINT32], int64(gc.Argsize(f.Type))) gins(arm.AMOVW, &con, &r) p := gins(arm.AMOVW, &r, nil) p.To.Type = obj.TYPE_MEM p.To.Reg = arm.REGSP p.To.Offset = 4 gins(arm.AMOVW, f, &r) p = gins(arm.AMOVW, &r, nil) p.To.Type = obj.TYPE_MEM p.To.Reg = arm.REGSP p.To.Offset = 8 regfree(&r) if proc == 1 { ginscall(gc.Newproc, 0) } else { ginscall(gc.Deferproc, 0) } if proc == 2 { gc.Nodconst(&con, gc.Types[gc.TINT32], 0) p := gins(arm.ACMP, &con, nil) p.Reg = arm.REG_R0 p = gc.Gbranch(arm.ABEQ, nil, +1) cgen_ret(nil) gc.Patch(p, gc.Pc) } } }