Skip to content

cmd/compile: ssa/phiopt: optimize packing repeated if conds for acculumated bitwise operators #80202

Description

@elee1766

Proposal Details

tldr: extend phiopt with

	//   if a { x = c } else { x = 0 } with x = (-bool2int(a)) & c
	//   if a { x |= c }  with x = x | ((-bool2int(a)) & c)
	//   if a { x ^= c }  with x = x ^ ((-bool2int(a)) & c)
	//   if a { x += c }  with x = x + ((-bool2int(a)) & c)

i was working on writing code that packed booleans into a uint16, and realised that i could not use bitwise operations to do this, and found this discussed in #9367 #64825

the response is that one should write something like

func bool2int(b bool) int {
    x := 0
    if b {
        x = 1
    }
    return x
}

which does properly get optimized to MOVBLZX

however, when writing code like

func packBool(a, b, c, d, e bool) (t uint16) {
	if a { t |= 1 << 0 }
	if b { t |= 1 << 1 }
	if c { t |= 1 << 2 }
	if d { t |= 1 << 3 }
	if e { t |= 1 << 5 }
	return
}

it is unable to get code that is as fast as using bitwise ops, see here: https://godbolt.org/z/aGY6Pbdob we see that it is a chain of TEST+CMOV, which is much slower than the shifts. (40% difference in performance on my machine)

instead of allowing the bool cast, i would like to propose that the go compiler should also be able to optimize for this use case.

right now, the existing phiopt https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/phiopt.go#L288 is only able to handle the case of constant pairs that are 0 and 1.

i would like to propose we add these two optimizations that should make this optimization possible

first, we i propose we allow the optimization of

var x uint32
if conf {
    x = 0xff
}

which is a TESTB+CMOV to

mask := -bool2int(cond)
x = mask ^ 0xFF

which is a NEGL+ANGL

second, i propose we allow the optimization of multiple Or/Xor/And into accumulator. for instance.

var x uint32
if a { x |= 1 }
if b { x |= 2 } 
if c { x |= 4 } 

to something like

x = bool2int(a)
x = x | (-bool2int(b) & 2)
x = x | (-bool2int(c) & 4)

with similar transformations which can also be done for xor and add-

x = x ^ ((-bool2int(a)) & c)
x = x + ((-bool2int(a)) & c)

i've tried to play around with doing this in this pr to my fork here: https://github.com/elee1766/go/pull/1/changes

and it seems to be able to produce the correct machine code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Performancecompiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    In Progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions