Skip to content

cmd/compile/internal/ssa: perform safe code de-duplication #24936

@quasilyte

Description

@quasilyte

There are cases where duplicated code sequences can be merged without performance loss or invalidation of panic line info.

The most obvious are BlockRet that do not contain anything that may cause run time panic.

Given this code:

package foo

func f1(v *int, y int) int {
	x := 214
	if *v == 0 {
		return x + y
	}
	if *v == 1 {
		return x + y
	}
	if *v == 2 {
		return x - y
	}
	if *v == 3 {
		return x - y
	}
	return 0
}

We currently get this output:

"".f1 STEXT nosplit size=115 args=0x18 locals=0x0
00000 (foo.go:3)	TEXT	"".f1(SB), NOSPLIT, $0-24
00000 (foo.go:3)	MOVQ	"".v+8(SP), AX
00005 (foo.go:5)	MOVQ	(AX), AX
00008 (foo.go:5)	TESTQ	AX, AX
00011 (foo.go:5)	JEQ	98
00013 (foo.go:8)	CMPQ	AX, $1
00017 (foo.go:8)	JEQ	81
00019 (foo.go:11)	CMPQ	AX, $2
00023 (foo.go:11)	JEQ	61
00025 (foo.go:14)	CMPQ	AX, $3
00029 (foo.go:14)	JNE	51
00031 (foo.go:15)	MOVQ	"".y+16(SP), AX
00036 (foo.go:15)	ADDQ	$-214, AX
00042 (foo.go:15)	NEGQ	AX
00045 (foo.go:15)	MOVQ	AX, "".~r2+24(SP)
00050 (foo.go:15)	RET
00051 (foo.go:17)	MOVQ	$0, "".~r2+24(SP)
00060 (foo.go:17)	RET
00061 (foo.go:12)	MOVQ	"".y+16(SP), AX
00066 (foo.go:12)	ADDQ	$-214, AX
00072 (foo.go:12)	NEGQ	AX
00075 (foo.go:12)	MOVQ	AX, "".~r2+24(SP)
00080 (foo.go:12)	RET
00081 (foo.go:9)	MOVQ	"".y+16(SP), AX
00086 (foo.go:9)	ADDQ	$214, AX
00092 (foo.go:9)	MOVQ	AX, "".~r2+24(SP)
00097 (foo.go:9)	RET
00098 (foo.go:6)	MOVQ	"".y+16(SP), AX
00103 (foo.go:6)	ADDQ	$214, AX
00109 (foo.go:6)	MOVQ	AX, "".~r2+24(SP)
00114 (foo.go:6)	RET

If we were merging epilogue sequences, we could get this:

"".f1 STEXT nosplit size=78 args=0x18 locals=0x0
00000 (foo.go:3)	TEXT	"".f1(SB), NOSPLIT, $0-24
00000 (foo.go:3)	MOVQ	"".v+8(SP), AX
00005 (foo.go:5)	MOVQ	(AX), AX
00008 (foo.go:5)	TESTQ	AX, AX
00011 (foo.go:5)	JEQ	61
00013 (foo.go:8)	CMPQ	AX, $1
00017 (foo.go:8)	JEQ	61
00019 (foo.go:11)	CMPQ	AX, $2
00023 (foo.go:11)	JEQ	41
00025 (foo.go:14)	CMPQ	AX, $3
00029 (foo.go:14)	JEQ	41
00031 (foo.go:17)	MOVQ	$0, "".~r2+24(SP)
00040 (foo.go:17)	RET
00041 (foo.go:12)	MOVQ	"".y+16(SP), AX
00046 (foo.go:12)	ADDQ	$-214, AX
00052 (foo.go:12)	NEGQ	AX
00055 (foo.go:12)	MOVQ	AX, "".~r2+24(SP)
00060 (foo.go:12)	RET
00061 (foo.go:6)	MOVQ	"".y+16(SP), AX
00066 (foo.go:6)	ADDQ	$214, AX
00072 (foo.go:6)	MOVQ	AX, "".~r2+24(SP)
00077 (foo.go:6)	RET

Proposed implementation will follow soon in form of CL.

Detailed bincmp info for go binary: bincmp.txt.

Main objectives/restrictions:

  • Do not introduce additional branches. Only change existing jump target to merged return.
  • Do not change program observable behavior (in particular, stack traces should also remain the same, source locations should be the same).

If any of those are not respected by implementation, it's a bug.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions