-
Notifications
You must be signed in to change notification settings - Fork 91
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
proposal: generate cpu feature-checks and runtime dispatch helpers #168
Comments
#171 added some manual CPU feature checks for examples. This is a reminder to replace that code if we implement an auto-generated solution for feature checks. |
As a simpler half-measure that also has the advantage of being able to drive test file generation, etc. I wonder about just specifying a new exported function in var irFile *ir.File
type FuncInfo struct {
Name string
Signature string
ISA []string
}
// Return the functions generated
func GeneratedFuncInfo() (retVal []FuncInfo) {
if irFile == nil {
return
}
for _, f := range irFile.Functions() {
retVal = append(retVal, FuncInfo{f.Name, f.Signature.String(), f.ISA})
}
return
} Questions: What is the preferred way to set
Thoughts? |
I quickly prototyped this using approach 2) above, and it works exactly as I need, e.g.: Generate()
for _, f := range GeneratedFuncInfo() {
fmt.Printf("Func: %s \t Sig: %s \t Reqs: %v\n", f.Name, f.Signature, f.ISA)
} Prints:
|
BTW, a much simpler way to enable all of this would be to just export the global context variable and let users go to town, buyer beware style. I suppose that's possible today by eschewing everything in |
After sleeping on this, I've spent a bit of time this morning trying out another approach to this issue. What I'm really trying to do here is gain hooks into (currently) internal Avo state that is needed to generate certain kinds of support code (runtime codepath selection, test coverage, automated benchmarking, etc.). My recent comments above articulate two possible approaches:
The third way I've just prototyped is to add an API call to register new file "Printers" using the existing internal hooks. In global.go: // AddPrinter registers a custom printer
func AddPrinter(flag, desc string, pB printer.Builder, dflt io.WriteCloser) {
pV := newPrinterValue(pB, dflt)
flagSet.Var(pV, flag, desc)
flags.printers = append(flags.printers, pV)
} Then in my code I can write, e.g. type myGenerator struct {
cfg printer.Config
printer.Generator
}
// NewMyGenerator constructs a printer for writing a function comments file.
func NewMyGenerator(cfg printer.Config) printer.Printer {
return &myGenerator{cfg: cfg}
}
func (gen *myGenerator) Print(f *ir.File) ([]byte, error) {
gen.Comment(gen.cfg.GeneratedWarning())
gen.NL()
gen.Printf("package %s\n", gen.cfg.Pkg)
for _, val := range f.Functions() {
gen.Comment(fmt.Sprintf("Func: %s \t Sig: %s \t Reqs: %v\n", val.Name, val.Signature, val.ISA))
}
return gen.Result()
} And in AddPrinter("myfile", "produce file enumerating generated functions in comments", NewMyGenerator, nil)
Generate() Which when run with flag woot.go // Code generated by command: go run generate_var_len_write.go -out var_len_write_amd64.s -stubs var_len_write_amd64.go -pkg prototype -myfile woot.go. DO NOT EDIT.
package prototype
// Func: varLenWriteAVX512_4 Sig: (in [][4]uint64, out *[4][]uint64, thresh uint64) byte Reqs: [AVX2 AVX512DQ AVX512F AVX512VL]
// Func: varLenWriteAVX2_4 Sig: (in [][4]uint64, out *[4][]uint64, thresh uint64) byte Reqs: [AVX AVX2 SSE2]
// Func: varLenWriteAVX512_8 Sig: (in [][8]uint64, out *[8][]uint64, thresh uint64) byte Reqs: [AVX512DQ AVX512F]
// Func: varLenWriteAVX2_8 Sig: (in [][8]uint64, out *[8][]uint64, thresh uint64) byte Reqs: [AVX AVX2 SSE2]
// Func: varLenWriteAVX512_16 Sig: (in [][16]uint64, out *[16][]uint64, thresh uint64) byte Reqs: [AVX512DQ AVX512F]
// Func: varLenWriteAVX2_16 Sig: (in [][16]uint64, out *[16][]uint64, thresh uint64) byte Reqs: [AVX AVX2 SSE2]
// Func: varLenWriteAVX512_24 Sig: (in [][24]uint64, out *[24][]uint64, thresh uint64) byte Reqs: [AVX512DQ AVX512F]
// Func: varLenWriteAVX512_32 Sig: (in [][32]uint64, out *[32][]uint64, thresh uint64) byte Reqs: [AVX512DQ AVX512F]
// Func: varLenWriteAVX512_48 Sig: (in [][48]uint64, out *[48][]uint64, thresh uint64) byte Reqs: [AVX512DQ AVX512F]
// Func: varLenWriteAVX512_64 Sig: (in [][64]uint64, out *[64][]uint64, thresh uint64) byte Reqs: [AVX512DQ AVX512F] That was way simpler than I thought it would be, and has the benefit of maybe preventing a lot of wheel reinvention around code generation. The principle drawbacks are:
My takeaway is that I think this Thoughts? |
@vsivsi suggested that
avo
could generate helpers for selecting function implementations based on runtime CPU feature checks (see #20 (comment)).This seems like a great idea but I think there are some questions about the details.
At a minimum,
avo
could generate boolean variables for each function indicating whether they are supported. This would be fairly easy:avo
already generates a comment for each function showing which ISAs it needs, and this would be enough to generate a boolean based on the constants inx/sys/cpu
.Generating runtime dispatch or function selection code might take a bit more thought, but also sounds doable.
Creating this issue for further discussion.
The text was updated successfully, but these errors were encountered: