Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: emit a safe point after every instruction #57719

Open
superbobry opened this issue Jan 10, 2023 · 7 comments
Open

cmd/compile: emit a safe point after every instruction #57719

superbobry opened this issue Jan 10, 2023 · 7 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@superbobry
Copy link

I am not familiar with Go compiler internals, so it is possible that the wording in the issue title is wrong. However, the request from the user perspective is to have a safe point for every place in the code where Delve can stop. This is not the case right now and occasionally Delve rejects evaluating an expression due to a missing safe point:

(dlv) call f.String()                                                                                                                                                                                               > foo/bar/bar.boo()
boo() foo/bar/boo.go:123 (PC: 0x55bb509b9466)                                                                
Command failed: call not at safe point 

I find it difficult to predict where safe points exists, so I just keep stepping forward and retrying the call command until it succeeds (not always the case). This significantly degrades the debugging experience for me, which is why this issue.

Note that a similar request was filed in go-delve/delve#3175 a few months ago, but it was closed because it requires a change in the Go compiler and not Delve.

@gopherbot gopherbot added this to the Proposal milestone Jan 10, 2023
@cherrymui
Copy link
Member

Actually, the compiler marks "unsafe points" for places that are not safe to be interrupted, and everywhere else is considered a safe point. In that sense, it is already safe point almost everywhere, except in a few specific places. The "call not at safe point" is due to that the stopped place is in the small range that are marked as an unsafe point. Could you show the source code and the disassembly of the stopped function? So we can see if there is anything we could do to improve the unsafe point situation. Thanks.

I'm not sure we can eliminate unsafe points entirely, as there are places where it is actually unsafe to make a call. Maybe certain functions are okay, but we don't know. Or maybe in some cases the debugger could single step to the end of the unsafe point (depending on the nature of the unsafe point, this may or may not change the program's state).

@ianlancetaylor
Copy link
Contributor

I'm taking this out of the proposal process, as it is not an API change.

@ianlancetaylor ianlancetaylor changed the title proposal: Emit a safe point after every instruction cme/compile: emit a safe point after every instruction Jan 10, 2023
@ianlancetaylor ianlancetaylor changed the title cme/compile: emit a safe point after every instruction cmd/compile: emit a safe point after every instruction Jan 10, 2023
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jan 10, 2023
@ianlancetaylor ianlancetaylor added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. and removed Proposal labels Jan 10, 2023
@ianlancetaylor ianlancetaylor modified the milestones: Proposal, Unplanned Jan 10, 2023
@cherrymui
Copy link
Member

Got a reproducer offline. It seems the compiler marks some seemingly safe code as unsafe points. Still looking into why this happens.

@randall77
Copy link
Contributor

FYI unsafe point marking for write barriers will change in 1.21, due to https://go-review.googlesource.com/c/go/+/447777 (and related CLs).

@cherrymui
Copy link
Member

Found the problem. For example, for this code

package p

var g int

func F(x []string) {
	g = 1
	for _, s := range x {
		f2(s)
	}
}

func f2(string)

it looks pretty safe. In particular, the store g=1 shouldn't be an unsafe point. However, it is currently marked as unsafe point:

    19        00025 (/tmp/x.go:7)	PCDATA	$0, $-2            // -2 is (starting) unsafe point
    19     -2 00025 (/tmp/x.go:7)	MOVQ	BX, p.x+56(SP)
    1e        00030 (/tmp/x.go:7)	PCDATA	$3, $2
    1e        00030 (/tmp/x.go:6)	MOVQ	$1, p.g(SB)        // this is the g=1 store
    29        00041 (/tmp/x.go:7)	XORL	CX, CX
    2b        00043 (/tmp/x.go:7)	JMP	96
    2d        00045 (/tmp/x.go:7)	MOVQ	CX, p..autotmp_9+16(SP)

This is due to

  • the compiler marks uintptr->unsafe.Pointer conversions as unsafe points, and back propagates the unsafeness to a "very safe" point, i.e. a call, or the function entry.
  • the code itself doesn't any unsafe, but currently the compiler generates unsafe pointer conversions for range loops, for the pointer increment of the loop variable.

Due to this, any code before a range loop from the last call or function entry would be marked unsafe.

The unsafe pointer conversions for range loop is from the IR lowering, where OCONVNOPs are emitted, which turn into SSA OpConvert. I wonder if we could somehow generate SSA OpAddPtr without the unsafe conversions? That would avoid this problem.

@cherrymui
Copy link
Member

It might also be possible to eliminate the uintptr->unsafe.Pointer marking. This is subtle. I'll need to think more carefully.

@gopherbot
Copy link

Change https://go.dev/cl/461696 mentions this issue: cmd/compile: don't mark uintptr->unsafe.Pointer conversion unsafe points

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants