├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── scanner.go ├── sse2 ├── bench_test.go ├── inst_amd64.go ├── inst_amd64.s └── inst_test.go ├── sse3 ├── inst_amd64.go ├── inst_amd64.s └── inst_test.go ├── sse41 ├── inst_amd64.go ├── inst_amd64.s └── inst_test.go ├── sse42 ├── inst_amd64.go ├── inst_amd64.s └── inst_test.go └── ssse3 ├── inst_amd64.go ├── inst_amd64.s └── inst_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | x86.csv 26 | x86desc.csv 27 | .tmp_scanner 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.8 5 | 6 | sudo: false 7 | script: make test 8 | 9 | branches: 10 | only: 11 | - master 12 | - stable 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Meng Zhuo 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: scanner 2 | $(foreach feature, $(feature_list), $(call gen, $(feature))) 3 | 4 | scanner: 5 | @rm -rf .tmp_scanner 6 | @go build -o .tmp_scanner scanner.go 7 | 8 | define gen 9 | @mkdir -p $1 10 | @./.tmp_scanner -out func -feature $1 > $1/inst_amd64.go 11 | @gofmt -w $1/inst_amd64.go 12 | 13 | @./.tmp_scanner -out asm -feature $1 > $1/inst_amd64.s 14 | @./.tmp_scanner -out test -feature $1 > $1/inst_test.go 15 | @gofmt -w $1/inst_test.go 16 | 17 | endef 18 | 19 | define testfunc 20 | cd $1 && go test -cover ; cd -; 21 | endef 22 | 23 | 24 | feature_list := sse2 sse3 ssse3 sse41 sse42 #avx avx2 25 | 26 | test: 27 | $(foreach feature, $(feature_list), $(call testfunc, $(feature))) 28 | 29 | clean: 30 | rm .tmp_scanner 31 | rm */inst_a* 32 | 33 | .PHONY: all clean test 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # intrinsic 3 | 4 | [![Build Status](https://travis-ci.org/mengzhuo/intrinsic.svg?branch=master)](https://travis-ci.org/mengzhuo/intrinsic) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/mengzhuo/intrinsic)](https://goreportcard.com/report/github.com/mengzhuo/intrinsic) 6 | 7 | Provide Golang native SIMD intrinsics on x86/amd64 platform 8 | 9 | * SSE2 [![godoc reference](https://godoc.org/github.com/mengzhuo/intrinsic/sse2?status.png)](https://godoc.org/github.com/mengzhuo/intrinsic/sse2) 10 | * SSE3 [![godoc reference](https://godoc.org/github.com/mengzhuo/intrinsic/sse3?status.png)](https://godoc.org/github.com/mengzhuo/intrinsic/sse3) 11 | * SSSE3 [![godoc reference](https://godoc.org/github.com/mengzhuo/intrinsic/ssse3?status.png)](https://godoc.org/github.com/mengzhuo/intrinsic/ssse3) 12 | * SSE41 [![godoc reference](https://godoc.org/github.com/mengzhuo/intrinsic/sse41?status.png)](https://godoc.org/github.com/mengzhuo/intrinsic/sse41) 13 | * SSE42 [![godoc reference](https://godoc.org/github.com/mengzhuo/intrinsic/sse42?status.png)](https://godoc.org/github.com/mengzhuo/intrinsic/sse42) 14 | 15 | ## Usage 16 | 17 | ```golang 18 | 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/mengzhuo/intrinsic/sse2" 25 | ) 26 | 27 | func main() { 28 | src := []float32{3.14, 2.17} 29 | dst := []float32{2.17, 3.15} 30 | sse2.MAXSDm64float32(src, dst) 31 | fmt.Print(src, dst) //[2.17 3.15] [2.17 3.15] 32 | } 33 | 34 | ``` 35 | 36 | ## Benchmarks 37 | 38 | SSE2 39 | it will provide about 6x-7x performance enhancement. 40 | 41 | ``` 42 | BenchmarkPMINUBByte-4 1000000000 2.65 ns/op 0 B/op 0 allocs/op 43 | BenchmarkGeneralPMINUBByte-4 100000000 15.8 ns/op 0 B/op 0 allocs/op 44 | BenchmarkPAND-4 1000000000 2.61 ns/op 0 B/op 0 allocs/op 45 | BenchmarkGeneralAND-4 100000000 15.4 ns/op 0 B/op 0 allocs/op 46 | ``` 47 | 48 | ## Development 49 | 50 | All codes in subdir is generated by scanner.go , see Makefile for more detail. 51 | 52 | x86.csv and x86desc.csv are from another repos in https://github.com/mengzhuo/x86data 53 | 54 | 55 | ## TODO 56 | 57 | - [ ] resolve immediate opcode generate 58 | - [ ] SSE2 gen=80, total=141, ratio=56.74% 59 | - [ ] SSE3 gen=6, total=10, ratio=60.00% 60 | - [ ] SSSE3 gen=15, total=32, ratio=46.88% 61 | - [ ] SSE4\_1 gen=26, total=49, ratio=53.06% 62 | - [ ] SSE4\_2 gen=1, total=5, ratio=20.00% 63 | - [ ] AVX gen=66, total=378, ratio=17.46% 64 | - [ ] AVX2 gen=8, total=159, ratio=5.03% 65 | - [ ] FMA 66 | -------------------------------------------------------------------------------- /scanner.go: -------------------------------------------------------------------------------- 1 | //+build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "bufio" 7 | "encoding/csv" 8 | "flag" 9 | "fmt" 10 | "log" 11 | "os" 12 | "sort" 13 | "strings" 14 | "text/template" 15 | ) 16 | 17 | var ( 18 | csvPath = flag.String("csv", "x86.csv", "csv file path") 19 | destPath = flag.String("desc", "x86desc.csv", "csv file path") 20 | outPath = flag.String("out", "func", "output file type") 21 | printFeature = flag.String("feature", "sse2", "print feature") 22 | plaform = flag.String("platform", "amd64", "") 23 | table = []*Inst{} 24 | featureMap = map[string][]*Inst{} 25 | descMap = map[string]string{} 26 | ) 27 | 28 | type Inst struct { 29 | Mnemonic, Encoding, Valid32, Valid64, Feature, Tags string 30 | 31 | Args []string 32 | FuncName string 33 | Register string 34 | } 35 | 36 | func ParseInst(l []string) (i *Inst) { 37 | 38 | if strings.Contains(l[5], "pseudo") { 39 | return 40 | } 41 | 42 | i = &Inst{ 43 | Mnemonic: l[0], 44 | Encoding: l[1], 45 | Valid32: l[2], 46 | Valid64: l[3], 47 | Feature: featureParse(l[4]), 48 | Tags: l[5]} 49 | 50 | ol := strings.Split(i.Mnemonic, " ") 51 | 52 | i.FuncName = ol[0] 53 | i.Args = ol[1:] 54 | for j, arg := range i.Args { 55 | arg = strings.Replace(arg, ",", "", 1) 56 | if slashI := strings.Index(arg, "/"); slashI != -1 { 57 | arg, i.Register = arg[:slashI], arg[slashI+1:] 58 | } 59 | t := arg 60 | switch t { 61 | case "xmm1": 62 | t = "X1" 63 | case "xmm": 64 | t = "X1" 65 | case "xmm2": 66 | t = "X2" 67 | case "xmm3": 68 | t = "X3" 69 | case "xmm4": 70 | t = "X4" 71 | case "mm1": 72 | t = "M1" 73 | case "mm2": 74 | t = "M2" 75 | case "r32": 76 | t = "CX" 77 | case "ymm1": 78 | t = "Y1" 79 | case "ymm2": 80 | t = "Y2" 81 | case "ymm3": 82 | t = "Y3" 83 | case "ymm4": 84 | t = "Y4" 85 | } 86 | i.Args[j] = t 87 | } 88 | return 89 | } 90 | 91 | func (i *Inst) String() string { 92 | if i.IsPacked() { 93 | return fmt.Sprintf("%10s %4v %4v", i.FuncName, i.Args, i.Target()) 94 | } 95 | return fmt.Sprintf("%s %v %s", i.FuncName, i.Args, i.Encoding) 96 | } 97 | 98 | func (i *Inst) CovertArgs(t string) string { 99 | 100 | args := make([]string, len(i.Args)) 101 | copy(args, i.Args) 102 | 103 | for i, k := range args { 104 | switch k { 105 | case "X1", "X2", "M1", "M2", "Y1", "Y2", "X3", "Y3": 106 | k = k + " []" + t 107 | case "r32": 108 | k = "r32 int32" 109 | case "imm8u": 110 | k = "imm8u uint8" 111 | } 112 | args[i] = k 113 | } 114 | return strings.Join(args, ", ") 115 | } 116 | 117 | func (i *Inst) TrueOpcode() string { 118 | var data []string 119 | switch i.FuncName[0] { 120 | case 'V': 121 | default: 122 | data = strings.Split(i.Encoding, " ") 123 | for i, v := range data { 124 | v = strings.TrimSpace(v) 125 | switch v { 126 | case "/r": 127 | v = "" 128 | default: 129 | v = "BYTE $0x" + v + ";" 130 | } 131 | data[i] = v 132 | } 133 | } 134 | return strings.Join(data, " ") 135 | } 136 | 137 | // the frame must be aligned as Go required 138 | func (i *Inst) FrameSize() (s int) { 139 | 140 | for _, a := range i.Args { 141 | switch a { 142 | case "X1", "X2", "M1", "M2", "Y1", "Y2", "Y3", "X3": 143 | s += 24 // slice size 24 144 | case "r32": 145 | s += 4 146 | case "imm8u": 147 | s += 1 148 | } 149 | } 150 | 151 | if s%8 == 0 { 152 | return s 153 | } 154 | return (s/8 + 1) * 8 155 | } 156 | 157 | func (i *Inst) IsPacked() bool { 158 | return i.Mnemonic[0] == 'P' 159 | } 160 | 161 | func (i *Inst) Description() string { 162 | if s, ok := descMap[i.FuncName]; ok { 163 | return s 164 | } 165 | return "" 166 | } 167 | 168 | func (i *Inst) Target() (t []string) { 169 | 170 | t = []string{`byte`} 171 | last := i.FuncName[len(i.FuncName)-1] 172 | dst := i.Description() 173 | if strings.Index(dst, "Integers") != -1 { 174 | if strings.Index(dst, "Unsigned") != -1 { 175 | if last == 'B' { 176 | t = append(t, "uint8") 177 | } 178 | if last == 'W' { 179 | t = append(t, "uint16") 180 | } 181 | if last == 'D' { 182 | t = append(t, "uint32") 183 | } 184 | if last == 'Q' { 185 | t = append(t, "int64") 186 | } 187 | } else { 188 | if last == 'B' { 189 | t = append(t, "int8") 190 | } 191 | if last == 'W' { 192 | t = append(t, "int16") 193 | } 194 | if last == 'D' { 195 | t = append(t, "int32") 196 | } 197 | if last == 'Q' { 198 | t = append(t, "int64") 199 | } 200 | } 201 | return 202 | } 203 | 204 | if strings.Index(dst, "Single-Precision Floating-Point") != -1 { 205 | t = append(t, "float32") 206 | return 207 | } 208 | if strings.Index(dst, "Single Precision Floating-Point") != -1 { 209 | t = append(t, "float32") 210 | return 211 | } 212 | if strings.Index(dst, "Single-FP") != -1 { 213 | t = append(t, "float32") 214 | return 215 | } 216 | 217 | if strings.Index(dst, "Double-Precision Floating-Point") != -1 { 218 | t = append(t, "float64") 219 | return 220 | } 221 | if strings.Index(dst, "Double Precision Floating-Point") != -1 { 222 | t = append(t, "float64") 223 | return 224 | } 225 | 226 | if strings.Index(dst, "Double-FP") != -1 { 227 | t = append(t, "float64") 228 | return 229 | } 230 | 231 | return 232 | } 233 | 234 | func main() { 235 | 236 | flag.Parse() 237 | 238 | readDesc() 239 | 240 | f, err := os.Open(*csvPath) 241 | if err != nil { 242 | log.Fatal(err) 243 | } 244 | rdr := csv.NewReader(f) 245 | 246 | records, err := rdr.ReadAll() 247 | if err != nil { 248 | log.Print(err) 249 | } 250 | for i, l := range records { 251 | if len(l) < 6 { 252 | log.Printf("line[%d], less than 6 items got=%s", i, l) 253 | continue 254 | } 255 | 256 | inst := ParseInst(l) 257 | if inst == nil { 258 | continue 259 | } 260 | table = append(table, inst) 261 | featureMap[inst.Feature] = append(featureMap[inst.Feature], inst) 262 | } 263 | 264 | if fm, ok := featureMap[*printFeature]; ok { 265 | makeInst(*printFeature, fm) 266 | } else { 267 | log.Fatalf("Feature:%s not existed", *printFeature) 268 | } 269 | } 270 | 271 | func readDesc() { 272 | f, err := os.Open(*destPath) 273 | if err != nil { 274 | log.Fatal(err) 275 | } 276 | scanner := bufio.NewScanner(f) 277 | for scanner.Scan() { 278 | 279 | index := strings.Index(scanner.Text(), "\t") 280 | if index <= 0 { 281 | log.Printf("text=%s, len=%d", scanner.Text(), index) 282 | continue 283 | } 284 | k := strings.TrimSpace(scanner.Text()[:index]) 285 | dsr := strings.TrimSpace(scanner.Text()[index:]) 286 | for _, item := range strings.Split(k, "/") { 287 | descMap[item] = dsr 288 | } 289 | } 290 | } 291 | 292 | func featureParse(s string) string { 293 | return strings.ToLower(strings.Replace(s, "_", "", 1)) 294 | } 295 | 296 | type Data struct { 297 | Platform string 298 | FeatureName string 299 | InstList []*Inst 300 | } 301 | 302 | var skipInst = map[string]int{ 303 | "MOV": 1, 304 | "CVT": 1, 305 | "VMOV": 1, 306 | "VCVT": 1, 307 | } 308 | 309 | func makeInst(feature string, instList []*Inst) { 310 | 311 | argsTable := map[string]int{} 312 | genInst := []*Inst{} 313 | skipedInst := []*Inst{} 314 | 315 | var gen int 316 | for _, inst := range instList { 317 | if _, ok := skipInst[inst.FuncName[:3]]; ok { 318 | continue 319 | } 320 | 321 | if len(inst.FuncName) >= 4 { 322 | if _, ok := skipInst[inst.FuncName[:4]]; ok { 323 | continue 324 | } 325 | } 326 | 327 | if inst.Valid64 == "V" { 328 | argsTable[fmt.Sprintf("%v", inst.Args)] += 1 329 | } 330 | 331 | switch fmt.Sprint(inst.Args) { 332 | case "[X1 X2]": 333 | case "[Y1 Y2]": 334 | case "[X1 X2 X3]": 335 | default: 336 | skipedInst = append(skipedInst, inst) 337 | continue 338 | } 339 | gen += 1 340 | genInst = append(genInst, inst) 341 | } 342 | sort.Sort(byName(genInst)) 343 | 344 | if os.Getenv("SHOW_SKIPPED") != "" { 345 | for _, inst := range skipedInst { 346 | log.Printf("%s skiped=%s %s(%d)", feature, inst.FuncName, inst.Args, len(inst.Args)) 347 | } 348 | } 349 | log.Printf("%s gen=%d, total=%d, ratio=%0.2f%%", feature, 350 | gen, len(instList), 351 | float64(gen)/float64(len(instList))*100) 352 | 353 | dat := &Data{*plaform, feature, genInst} 354 | tmpl := funcTmpl 355 | switch *outPath { 356 | case "asm": 357 | tmpl = asmTmpl 358 | case "test": 359 | tmpl = testTmpl 360 | } 361 | tt, err := template.New("root").Parse(tmpl) 362 | if err != nil { 363 | log.Fatal(err) 364 | } 365 | err = tt.Execute(os.Stdout, dat) 366 | if err != nil { 367 | log.Fatal(err) 368 | } 369 | } 370 | 371 | func (i *Inst) ArgsToAsm() string { 372 | buf := []string{} 373 | switch fmt.Sprintf("%v", i.Args) { 374 | case "[X1 X2]": 375 | if _, in := unrecognized[i.FuncName]; in { 376 | return fmt.Sprintf(X1X2Raw, i.TrueOpcode()) 377 | } 378 | return fmt.Sprintf(X1X2, i.FuncName) 379 | 380 | case "[X1 X2 X3]": 381 | if _, in := unrecognized[i.FuncName]; in { 382 | return fmt.Sprintf(X1X2X3, i.TrueOpcode()) 383 | } 384 | return fmt.Sprintf(X1X2X3, i.FuncName) 385 | 386 | case "[Y1 Y2]": 387 | if _, in := unrecognized[i.FuncName]; in { 388 | return fmt.Sprintf(Y1Y2Raw, i.TrueOpcode()) 389 | } 390 | return fmt.Sprintf(Y1Y2, i.FuncName) 391 | 392 | default: 393 | log.Printf("unsupported args, %s", i.FuncName) 394 | } 395 | return strings.Join(buf, "\n\t") 396 | } 397 | 398 | func set(s string) (r map[string]struct{}) { 399 | r = map[string]struct{}{} 400 | for _, n := range strings.Split(s, "\n") { 401 | n = strings.TrimSpace(n) 402 | if n == "" { 403 | continue 404 | } 405 | if n[:2] == "//" { 406 | continue 407 | } 408 | r[n] = struct{}{} 409 | } 410 | return 411 | } 412 | 413 | const funcTmpl = `package {{.FeatureName}} 414 | 415 | {{ range $index, $inst := .InstList }} 416 | {{ range $target := .Target }} 417 | // go:noescape 418 | 419 | // {{$inst.FuncName}}{{$inst.Register}}{{$target}} {{$inst.Description}} 420 | func {{$inst.FuncName}}{{$inst.Register}}{{$target}}({{$inst.CovertArgs $target}}) 421 | {{end}}{{end}} 422 | ` 423 | 424 | const X1X2 = `FPTOX1X2 425 | %s X2, X1 426 | RETX1X2 427 | ` 428 | 429 | const X1X2Raw = `FPTOX1X2 430 | %s BYTE $0xca // $0xca = X2, X1 431 | RETX1X2 432 | ` 433 | const Y1Y2 = ` 434 | FPTOY1Y2 435 | %s Y2, Y1 436 | MOVOU Y1, (SI) 437 | RET 438 | ` 439 | 440 | const X1X2X3 = ` 441 | FPTOX1X2X3 442 | %s X3, X2, X1 443 | MOVOU X1, (SI) 444 | MOVOU X2, (DI) 445 | RET 446 | ` 447 | 448 | const Y1Y2Raw = ` 449 | FPTOY1Y2 450 | %s BYTE $0xca // $0xca = X2, X1 451 | MOVOU Y1, (SI) 452 | RET 453 | ` 454 | const asmTmpl = `#include "textflag.h" 455 | 456 | #define FPTOX1X2 \ 457 | MOVQ a+0(FP), SI;\ 458 | MOVQ b+24(FP), DI;\ 459 | MOVOU (SI), X1;\ 460 | MOVOU (DI), X2;\ 461 | 462 | #define RETX1X2 \ 463 | MOVOU X1, (SI);\ 464 | MOVOU X2, (DI);\ 465 | RET;\ 466 | 467 | #define FPTOY1Y2 \ 468 | MOVQ a+0(FP), SI;\ 469 | MOVQ b+24(FP), DI;\ 470 | MOVOU (SI), Y1;\ 471 | MOVOU (DI), Y2;\ 472 | 473 | #define FPTOX1X2X3 \ 474 | MOVQ a+0(FP), SI;\ 475 | MOVQ b+24(FP), DI;\ 476 | MOVOU (SI), X1;\ 477 | MOVOU (DI), X2;\ 478 | MOVQ c+48(FP), DI;\ 479 | MOVOU (DI), X3;\ 480 | 481 | #define RETY1Y2 \ 482 | MOVOU Y1, (SI);\ 483 | MOVOU Y2, (DI);\ 484 | RET;\ 485 | 486 | {{ range $index, $inst := .InstList }} 487 | {{ range $target := .Target }} 488 | TEXT ·{{$inst.FuncName}}{{$inst.Register}}{{$target}}(SB),NOSPLIT,$0-{{$inst.FrameSize}} 489 | {{$inst.ArgsToAsm}} 490 | {{end}}{{end}} 491 | 492 | ` 493 | 494 | var unrecognized = set(` 495 | PACKSSDW 496 | PCMPEQD 497 | PCMPGTD 498 | PMADDWD 499 | PMADDWD 500 | PMULUDQ 501 | PMULUDQ 502 | PSLLD 503 | PSRAD 504 | PSRLD 505 | PSUBD 506 | PUNPCKHDQ 507 | PUNPCKHWD 508 | PUNPCKLDQ 509 | PUNPCKLWD 510 | ADDSUBPD 511 | ADDSUBPS 512 | PABSB 513 | PABSD 514 | PABSW 515 | PMADDUBSW 516 | PMULHRSW 517 | PSIGNB 518 | PSIGND 519 | PSIGNW 520 | PACKUSDW 521 | PCMPEQQ 522 | PMAXSB 523 | PMAXSB 524 | PMAXSD 525 | PMAXSD 526 | PMAXUD 527 | PMAXUD 528 | PMAXUW 529 | PMAXUW 530 | PMINSB 531 | PMINSD 532 | PMINSD 533 | PMINUD 534 | PMINUD 535 | PMINUW 536 | PMINUW 537 | PTEST 538 | PCMPGTQ 539 | `) 540 | 541 | const testTmpl = `package {{.FeatureName}} 542 | 543 | import ( 544 | "testing" 545 | "strings" 546 | ) 547 | func sh(s string) bool { 548 | 549 | sss := []string{"Low", "High", "Test"} 550 | 551 | for _, cmp := range sss { 552 | if strings.Index(s, cmp) != -1 { 553 | return true 554 | } 555 | } 556 | return false 557 | } 558 | 559 | {{ range $index, $inst := .InstList }} 560 | {{ range $target := .Target }} 561 | 562 | func Test{{$inst.FuncName}}{{$inst.Register}}{{$target}}(t *testing.T){ 563 | a := make([]{{$target}}, 64) 564 | aT := make([]{{$target}}, 64) 565 | for i:=0;i