├── CONTRIBUTING.md ├── LICENSE ├── PATENTS ├── README.md ├── arm ├── arm.csv ├── armasm │ ├── Makefile │ ├── decode.go │ ├── decode_test.go │ ├── ext_test.go │ ├── gnu.go │ ├── inst.go │ ├── objdump_test.go │ ├── objdumpext_test.go │ ├── plan9x.go │ ├── tables.go │ └── testdata │ │ ├── Makefile │ │ └── decode.txt ├── armmap │ └── map.go └── armspec │ ├── spec.go │ └── specmap.go ├── arm64 ├── arm64asm │ ├── arg.go │ ├── condition.go │ ├── condition_util.go │ ├── decode.go │ ├── decode_test.go │ ├── ext_test.go │ ├── gnu.go │ ├── inst.go │ ├── inst.json │ ├── objdump_test.go │ ├── objdumpext_test.go │ ├── plan9x.go │ ├── tables.go │ └── testdata │ │ ├── Makefile │ │ ├── gnucases.txt │ │ └── plan9cases.txt ├── arm64gen │ └── sysreggen.go └── arm64spec │ └── spec.go ├── codereview.cfg ├── go.mod ├── go.sum ├── internal ├── simdgen │ ├── .gitignore │ ├── asm.yaml.toy │ ├── categories.yaml │ ├── etetest.sh │ ├── gen_simdGenericOps.go │ ├── gen_simdIntrinsics.go │ ├── gen_simdMachineOps.go │ ├── gen_simdTypes.go │ ├── gen_simdrules.go │ ├── gen_simdssa.go │ ├── gen_utility.go │ ├── go.yaml │ ├── godefs.go │ ├── main.go │ ├── ops │ │ ├── AddSub │ │ │ ├── categories.yaml │ │ │ └── go.yaml │ │ ├── BitwiseLogic │ │ │ ├── categories.yaml │ │ │ └── go.yaml │ │ ├── Compares │ │ │ ├── categories.yaml │ │ │ └── go.yaml │ │ ├── FPonlyArith │ │ │ ├── categories.yaml │ │ │ └── go.yaml │ │ ├── MinMax │ │ │ ├── categories.yaml │ │ │ └── go.yaml │ │ ├── Mul │ │ │ ├── categories.yaml │ │ │ └── go.yaml │ │ └── main.go │ ├── types.yaml │ └── xed.go └── unify │ ├── closure.go │ ├── domain.go │ ├── dot.go │ ├── env.go │ ├── html.go │ ├── pos.go │ ├── testdata │ ├── unify.yaml │ └── vars.yaml │ ├── trace.go │ ├── unify.go │ ├── unify_test.go │ ├── value.go │ ├── value_test.go │ ├── yaml.go │ └── yaml_test.go ├── loong64 ├── loong64asm │ ├── arg.go │ ├── decode.go │ ├── decode_test.go │ ├── ext_test.go │ ├── gnu.go │ ├── inst.go │ ├── objdump_test.go │ ├── objdumpext_test.go │ ├── plan9x.go │ ├── tables.go │ └── testdata │ │ ├── gnucases.txt │ │ └── plan9cases.txt └── loong64spec │ └── spec.go ├── ppc64 ├── pp64.csv ├── ppc64asm │ ├── decode.go │ ├── decode_test.go │ ├── doc.go │ ├── ext_test.go │ ├── field.go │ ├── field_test.go │ ├── gnu.go │ ├── inst.go │ ├── objdump_test.go │ ├── objdumpext_test.go │ ├── plan9.go │ ├── tables.go │ └── testdata │ │ ├── decode.txt │ │ ├── decode_branch.txt │ │ └── decode_generated.txt ├── ppc64map │ └── map.go ├── ppc64spec │ └── spec.go └── ppc64util │ ├── hack.h │ └── util.go ├── riscv64 ├── riscv64asm │ ├── arg.go │ ├── csr_string.go │ ├── decode.go │ ├── decode_test.go │ ├── ext_test.go │ ├── gnu.go │ ├── inst.go │ ├── objdump_test.go │ ├── objdumpext_test.go │ ├── plan9x.go │ ├── tables.go │ └── testdata │ │ ├── Makefile │ │ ├── gnucases.txt │ │ └── plan9cases.txt └── riscv64spec │ └── spec.go ├── s390x ├── s390x.csv ├── s390xasm │ ├── Makefile │ ├── decode.go │ ├── decode_test.go │ ├── field.go │ ├── gnu.go │ ├── inst.go │ ├── plan9.go │ ├── tables.go │ └── testdata │ │ ├── decode.txt │ │ └── decode_generated.txt ├── s390xmap │ └── map.go ├── s390xspec │ └── spec.go └── s390xutil │ ├── hack.h │ └── util.go └── x86 ├── x86.csv ├── x86.v0.2.csv ├── x86asm ├── Makefile ├── decode.go ├── decode_test.go ├── ext_test.go ├── format_test.go ├── gnu.go ├── inst.go ├── inst_test.go ├── intel.go ├── objdump_test.go ├── objdumpext_test.go ├── plan9ext_test.go ├── plan9x.go ├── plan9x_test.go ├── tables.go ├── testdata │ ├── Makefile │ ├── decode.txt │ └── libmach8db.c ├── xed_test.go └── xedext_test.go ├── x86avxgen ├── avxgen_test.go ├── decode.go ├── generate.go ├── instruction.go ├── main.go ├── print.go └── testdata │ ├── golden.txt │ └── xedpath │ ├── all-dec-instructions.txt │ ├── all-element-types.txt │ ├── all-extra-widths.txt │ ├── all-state.txt │ └── all-widths.txt ├── x86csv ├── reader.go ├── x86csv.go └── x86csv_test.go ├── x86map └── map.go ├── x86spec ├── .gitignore ├── cleanup.go ├── format.go ├── parse.go ├── spec.go └── spec_test.go └── xeddata ├── database.go ├── doc.go ├── example_test.go ├── object.go ├── operand.go ├── pattern_set.go ├── reader.go ├── readlines.go ├── testdata ├── xed_objects.txt └── xedpath │ ├── all-element-types.txt │ ├── all-extra-widths.txt │ ├── all-state.txt │ └── all-widths.txt ├── xeddata.go └── xeddata_test.go /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Go 2 | 3 | Go is an open source project. 4 | 5 | It is the work of hundreds of contributors. We appreciate your help! 6 | 7 | ## Filing issues 8 | 9 | When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: 10 | 11 | 1. What version of Go are you using (`go version`)? 12 | 2. What operating system and processor architecture are you using? 13 | 3. What did you do? 14 | 4. What did you expect to see? 15 | 5. What did you see instead? 16 | 17 | General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. 18 | The gophers there will answer or ask you to file an issue if you've tripped over a bug. 19 | 20 | ## Contributing code 21 | 22 | Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) 23 | before sending patches. 24 | 25 | Unless otherwise noted, the Go source files are distributed under 26 | the BSD-style license found in the LICENSE file. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 The Go Authors. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google LLC nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arch 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/golang.org/x/arch.svg)](https://pkg.go.dev/golang.org/x/arch) 4 | 5 | This repository holds machine architecture information used by the Go toolchain. 6 | The parts needed in the main Go repository are copied in. 7 | 8 | ## Report Issues / Send Patches 9 | 10 | This repository uses Gerrit for code changes. To learn how to submit changes to 11 | this repository, see https://go.dev/doc/contribute. 12 | 13 | The git repository is https://go.googlesource.com/arch. 14 | 15 | The main issue tracker for the arch repository is located at 16 | https://go.dev/issues. Prefix your issue with "x/arch:" in the 17 | subject line, so it is easy to find. 18 | -------------------------------------------------------------------------------- /arm/armasm/Makefile: -------------------------------------------------------------------------------- 1 | tables.go: ../armmap/map.go ../arm.csv 2 | go run ../armmap/map.go -fmt=decoder ../arm.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go 3 | -------------------------------------------------------------------------------- /arm/armasm/decode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package armasm 6 | 7 | import ( 8 | "encoding/hex" 9 | "io/ioutil" 10 | "strconv" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | func TestDecode(t *testing.T) { 16 | data, err := ioutil.ReadFile("testdata/decode.txt") 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | all := string(data) 21 | for strings.Contains(all, "\t\t") { 22 | all = strings.Replace(all, "\t\t", "\t", -1) 23 | } 24 | for _, line := range strings.Split(all, "\n") { 25 | line = strings.TrimSpace(line) 26 | if line == "" || strings.HasPrefix(line, "#") { 27 | continue 28 | } 29 | f := strings.SplitN(line, "\t", 4) 30 | i := strings.Index(f[0], "|") 31 | if i < 0 { 32 | t.Errorf("parsing %q: missing | separator", f[0]) 33 | continue 34 | } 35 | if i%2 != 0 { 36 | t.Errorf("parsing %q: misaligned | separator", f[0]) 37 | } 38 | size := i / 2 39 | code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) 40 | if err != nil { 41 | t.Errorf("parsing %q: %v", f[0], err) 42 | continue 43 | } 44 | mode, err := strconv.Atoi(f[1]) 45 | if err != nil { 46 | t.Errorf("invalid mode %q in: %s", f[1], line) 47 | continue 48 | } 49 | syntax, asm := f[2], f[3] 50 | inst, err := Decode(code, Mode(mode)) 51 | var out string 52 | if err != nil { 53 | out = "error: " + err.Error() 54 | } else { 55 | switch syntax { 56 | case "gnu": 57 | out = GNUSyntax(inst) 58 | case "plan9": // [sic] 59 | out = GoSyntax(inst, 0, nil, nil) 60 | default: 61 | t.Errorf("unknown syntax %q", syntax) 62 | continue 63 | } 64 | } 65 | if out != asm || inst.Len != size { 66 | t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /arm/armasm/gnu.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package armasm 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "strings" 11 | ) 12 | 13 | var saveDot = strings.NewReplacer( 14 | ".F16", "_dot_F16", 15 | ".F32", "_dot_F32", 16 | ".F64", "_dot_F64", 17 | ".S32", "_dot_S32", 18 | ".U32", "_dot_U32", 19 | ".FXS", "_dot_S", 20 | ".FXU", "_dot_U", 21 | ".32", "_dot_32", 22 | ) 23 | 24 | // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. 25 | // This form typically matches the syntax defined in the ARM Reference Manual. 26 | func GNUSyntax(inst Inst) string { 27 | var buf bytes.Buffer 28 | op := inst.Op.String() 29 | op = saveDot.Replace(op) 30 | op = strings.Replace(op, ".", "", -1) 31 | op = strings.Replace(op, "_dot_", ".", -1) 32 | op = strings.ToLower(op) 33 | buf.WriteString(op) 34 | sep := " " 35 | for i, arg := range inst.Args { 36 | if arg == nil { 37 | break 38 | } 39 | text := gnuArg(&inst, i, arg) 40 | if text == "" { 41 | continue 42 | } 43 | buf.WriteString(sep) 44 | sep = ", " 45 | buf.WriteString(text) 46 | } 47 | return buf.String() 48 | } 49 | 50 | func gnuArg(inst *Inst, argIndex int, arg Arg) string { 51 | switch inst.Op &^ 15 { 52 | case LDRD_EQ, LDREXD_EQ, STRD_EQ: 53 | if argIndex == 1 { 54 | // second argument in consecutive pair not printed 55 | return "" 56 | } 57 | case STREXD_EQ: 58 | if argIndex == 2 { 59 | // second argument in consecutive pair not printed 60 | return "" 61 | } 62 | } 63 | 64 | switch arg := arg.(type) { 65 | case Imm: 66 | switch inst.Op &^ 15 { 67 | case BKPT_EQ: 68 | return fmt.Sprintf("%#04x", uint32(arg)) 69 | case SVC_EQ: 70 | return fmt.Sprintf("%#08x", uint32(arg)) 71 | } 72 | return fmt.Sprintf("#%d", int32(arg)) 73 | 74 | case ImmAlt: 75 | return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot) 76 | 77 | case Mem: 78 | R := gnuArg(inst, -1, arg.Base) 79 | X := "" 80 | if arg.Sign != 0 { 81 | X = "" 82 | if arg.Sign < 0 { 83 | X = "-" 84 | } 85 | X += gnuArg(inst, -1, arg.Index) 86 | if arg.Shift == ShiftLeft && arg.Count == 0 { 87 | // nothing 88 | } else if arg.Shift == RotateRightExt { 89 | X += ", rrx" 90 | } else { 91 | X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count) 92 | } 93 | } else { 94 | X = fmt.Sprintf("#%d", arg.Offset) 95 | } 96 | 97 | switch arg.Mode { 98 | case AddrOffset: 99 | if X == "#0" { 100 | return fmt.Sprintf("[%s]", R) 101 | } 102 | return fmt.Sprintf("[%s, %s]", R, X) 103 | case AddrPreIndex: 104 | return fmt.Sprintf("[%s, %s]!", R, X) 105 | case AddrPostIndex: 106 | return fmt.Sprintf("[%s], %s", R, X) 107 | case AddrLDM: 108 | if X == "#0" { 109 | return R 110 | } 111 | case AddrLDM_WB: 112 | if X == "#0" { 113 | return R + "!" 114 | } 115 | } 116 | return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X) 117 | 118 | case PCRel: 119 | return fmt.Sprintf(".%+#x", int32(arg)+4) 120 | 121 | case Reg: 122 | switch inst.Op &^ 15 { 123 | case LDREX_EQ: 124 | if argIndex == 0 { 125 | return fmt.Sprintf("r%d", int32(arg)) 126 | } 127 | } 128 | switch arg { 129 | case R10: 130 | return "sl" 131 | case R11: 132 | return "fp" 133 | case R12: 134 | return "ip" 135 | } 136 | 137 | case RegList: 138 | var buf bytes.Buffer 139 | fmt.Fprintf(&buf, "{") 140 | sep := "" 141 | for i := 0; i < 16; i++ { 142 | if arg&(1<&1 | tee log 3 | cd ..; go test -cover -run 'ObjdumpARMUncond' -v -timeout 10h -printtests -long 2>&1 | tee -a log 4 | egrep ' (gnu|plan9) ' ../log |sort >newdecode.txt 5 | 6 | -------------------------------------------------------------------------------- /arm/armspec/specmap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | package main 8 | 9 | import ( 10 | "encoding/json" 11 | "fmt" 12 | "io/ioutil" 13 | "log" 14 | "os" 15 | "regexp" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | var _ = os.Stdout 21 | var _ = fmt.Sprintf 22 | 23 | type Inst struct { 24 | Name string 25 | ID string 26 | Bits string 27 | Arch string 28 | Syntax []string 29 | Code string 30 | Base uint32 31 | Mask uint32 32 | Prog []*Stmt 33 | } 34 | 35 | func main() { 36 | data, err := ioutil.ReadFile("spec.json") 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | var insts []Inst 41 | if err := json.Unmarshal(data, &insts); err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | var out []Inst 46 | for _, inst := range insts { 47 | inst.Prog = parse(inst.Name+" "+inst.ID, inst.Code) 48 | if inst.ID[0] == 'A' && !strings.HasPrefix(inst.Syntax[0], "MSR") && !strings.Contains(inst.Syntax[0], "") && !strings.Contains(inst.Syntax[0], "VLDM") && !strings.Contains(inst.Syntax[0], "VSTM") { 49 | out = append(out, inst) 50 | } 51 | } 52 | insts = out 53 | 54 | for i := range insts { 55 | dosize(&insts[i]) 56 | } 57 | 58 | var cond, special []Inst 59 | for _, inst := range insts { 60 | if inst.Base>>28 == 0xF { 61 | special = append(special, inst) 62 | } else { 63 | cond = append(cond, inst) 64 | } 65 | } 66 | 67 | fmt.Printf("special:\n") 68 | split(special, 0xF0000000, 1) 69 | fmt.Printf("cond:\n") 70 | split(cond, 0xF0000000, 1) 71 | } 72 | 73 | func dosize(inst *Inst) { 74 | var base, mask uint32 75 | off := 0 76 | for _, f := range strings.Split(inst.Bits, "|") { 77 | if i := strings.Index(f, ":"); i >= 0 { 78 | n, _ := strconv.Atoi(f[i+1:]) 79 | off += n 80 | continue 81 | } 82 | for _, bit := range strings.Fields(f) { 83 | switch bit { 84 | case "0", "(0)": 85 | mask |= 1 << uint(31-off) 86 | case "1", "(1)": 87 | base |= 1 << uint(31-off) 88 | } 89 | off++ 90 | } 91 | } 92 | if off != 16 && off != 32 { 93 | log.Printf("incorrect bit count for %s %s: have %d", inst.Name, inst.Bits, off) 94 | } 95 | if off == 16 { 96 | mask >>= 16 97 | base >>= 16 98 | } 99 | mask |= base 100 | inst.Mask = mask 101 | inst.Base = base 102 | } 103 | 104 | func split(insts []Inst, used uint32, depth int) { 105 | Again: 106 | if len(insts) <= 1 { 107 | for _, inst := range insts { 108 | fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1)) 109 | } 110 | return 111 | } 112 | 113 | m := ^used 114 | for _, inst := range insts { 115 | m &= inst.Mask 116 | } 117 | if m == 0 { 118 | fmt.Printf("«%*s%#08x masked out (%d)\n", depth*2, "", used, len(insts)) 119 | for _, inst := range insts { 120 | fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1)) 121 | } 122 | updated := false 123 | for i := range insts { 124 | if updateMask(&insts[i]) { 125 | updated = true 126 | } 127 | } 128 | fmt.Printf("»\n") 129 | if updated { 130 | goto Again 131 | } 132 | fmt.Printf("%*s%#08x masked out (%d)\n", depth*2, "", used, len(insts)) 133 | for _, inst := range insts { 134 | fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1)) 135 | } 136 | //checkOverlap(used, insts) 137 | return 138 | } 139 | for i := 31; i >= 0; i-- { 140 | if m&(1< 0 { 153 | suffix := "" 154 | if len(bit[1-b]) == 0 { 155 | suffix = " (only)" 156 | } 157 | fmt.Printf("%*sbit %#08x = %d%s\n", depth*2, "", m, b, suffix) 158 | split(list, used|m, depth+1) 159 | } 160 | } 161 | } 162 | 163 | var seeRE = regexp.MustCompile(`SEE ([^;\n]+)`) 164 | 165 | func updateMask(inst *Inst) bool { 166 | defer func() { 167 | if err := recover(); err != nil { 168 | fmt.Println("PANIC:", err) 169 | return 170 | } 171 | }() 172 | 173 | print(".") 174 | println(inst.Name, inst.ID, inst.Bits) 175 | println(inst.Code) 176 | wiggle := ^inst.Mask &^ 0xF0000000 177 | n := countbits(wiggle) 178 | m1 := ^uint32(0) 179 | m2 := ^uint32(0) 180 | for i := uint32(0); i < 1<>= 1 { 207 | n += int(x & 1) 208 | } 209 | return n 210 | } 211 | 212 | func expand(x, m uint32) uint32 { 213 | var out uint32 214 | for i := uint(0); i < 32; i++ { 215 | out >>= 1 216 | if m&1 != 0 { 217 | out |= (x & 1) << 31 218 | x >>= 1 219 | } 220 | m >>= 1 221 | } 222 | return out 223 | } 224 | -------------------------------------------------------------------------------- /arm64/arm64asm/condition_util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package arm64asm 6 | 7 | func extract_bit(value, bit uint32) uint32 { 8 | return (value >> bit) & 1 9 | } 10 | 11 | func bfxpreferred_4(sf, opc1, imms, immr uint32) bool { 12 | if imms < immr { 13 | return false 14 | } 15 | if (imms>>5 == sf) && (imms&0x1f == 0x1f) { 16 | return false 17 | } 18 | if immr == 0 { 19 | if sf == 0 && (imms == 7 || imms == 15) { 20 | return false 21 | } 22 | if sf == 1 && opc1 == 0 && (imms == 7 || 23 | imms == 15 || imms == 31) { 24 | return false 25 | } 26 | } 27 | return true 28 | } 29 | 30 | func move_wide_preferred_4(sf, N, imms, immr uint32) bool { 31 | if sf == 1 && N != 1 { 32 | return false 33 | } 34 | if sf == 0 && !(N == 0 && ((imms>>5)&1) == 0) { 35 | return false 36 | } 37 | if imms < 16 { 38 | return (-immr)%16 <= (15 - imms) 39 | } 40 | width := uint32(32) 41 | if sf == 1 { 42 | width = uint32(64) 43 | } 44 | if imms >= (width - 15) { 45 | return (immr % 16) <= (imms - (width - 15)) 46 | } 47 | return false 48 | } 49 | 50 | type sys uint8 51 | 52 | const ( 53 | sys_AT sys = iota 54 | sys_DC 55 | sys_IC 56 | sys_TLBI 57 | sys_SYS 58 | ) 59 | 60 | func sys_op_4(op1, crn, crm, op2 uint32) sys { 61 | sysInst := sysInstFields{uint8(op1), uint8(crn), uint8(crm), uint8(op2)} 62 | return sysInst.getType() 63 | } 64 | 65 | func is_zero(x uint32) bool { 66 | return x == 0 67 | } 68 | 69 | func is_ones_n16(x uint32) bool { 70 | return x == 0xffff 71 | } 72 | 73 | func bit_count(x uint32) uint8 { 74 | var count uint8 75 | for count = 0; x > 0; x >>= 1 { 76 | if (x & 1) == 1 { 77 | count++ 78 | } 79 | } 80 | return count 81 | } 82 | -------------------------------------------------------------------------------- /arm64/arm64asm/decode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package arm64asm 6 | 7 | import ( 8 | "encoding/hex" 9 | "io/ioutil" 10 | "path/filepath" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | func testDecode(t *testing.T, syntax string) { 16 | input := filepath.Join("testdata", syntax+"cases.txt") 17 | data, err := ioutil.ReadFile(input) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | all := string(data) 22 | for strings.Contains(all, "\t\t") { 23 | all = strings.Replace(all, "\t\t", "\t", -1) 24 | } 25 | for _, line := range strings.Split(all, "\n") { 26 | line = strings.TrimSpace(line) 27 | if line == "" || strings.HasPrefix(line, "#") { 28 | continue 29 | } 30 | f := strings.SplitN(line, "\t", 2) 31 | i := strings.Index(f[0], "|") 32 | if i < 0 { 33 | t.Errorf("parsing %q: missing | separator", f[0]) 34 | continue 35 | } 36 | if i%2 != 0 { 37 | t.Errorf("parsing %q: misaligned | separator", f[0]) 38 | } 39 | code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) 40 | if err != nil { 41 | t.Errorf("parsing %q: %v", f[0], err) 42 | continue 43 | } 44 | asm := f[1] 45 | inst, decodeErr := Decode(code) 46 | if decodeErr != nil && decodeErr != errUnknown { 47 | // Some rarely used system instructions are not supported 48 | // Following logicals will filter such unknown instructions 49 | 50 | t.Errorf("parsing %x: %s", code, decodeErr) 51 | continue 52 | } 53 | var out string 54 | switch syntax { 55 | case "gnu": 56 | out = GNUSyntax(inst) 57 | case "plan9": 58 | out = GoSyntax(inst, 0, nil, nil) 59 | default: 60 | t.Errorf("unknown syntax %q", syntax) 61 | continue 62 | } 63 | // TODO: system instruction. 64 | var Todo = strings.Fields(` 65 | sys 66 | at 67 | ic 68 | hvc 69 | smc 70 | `) 71 | if strings.Replace(out, " ", "", -1) != strings.Replace(asm, " ", "", -1) && !hasPrefix(asm, Todo...) { 72 | // Exclude MSR since GNU objdump result is incorrect. eg. 0xd504431f msr s0_4_c4_c3_0, xzr 73 | if !strings.HasSuffix(asm, " nv") && !strings.HasPrefix(asm, "msr") { 74 | t.Errorf("Decode(%s) [%s] = %s, want %s", strings.Trim(f[0], "|"), syntax, out, asm) 75 | } 76 | } 77 | } 78 | } 79 | 80 | func TestDecodeGNUSyntax(t *testing.T) { 81 | testDecode(t, "gnu") 82 | } 83 | 84 | func TestDecodeGoSyntax(t *testing.T) { 85 | testDecode(t, "plan9") 86 | } 87 | -------------------------------------------------------------------------------- /arm64/arm64asm/gnu.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package arm64asm 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. 12 | // This form typically matches the syntax defined in the ARM Reference Manual. 13 | func GNUSyntax(inst Inst) string { 14 | switch inst.Op { 15 | case RET: 16 | if r, ok := inst.Args[0].(Reg); ok && r == X30 { 17 | return "ret" 18 | } 19 | case B: 20 | if _, ok := inst.Args[0].(Cond); ok { 21 | return strings.ToLower("b." + inst.Args[0].String() + " " + inst.Args[1].String()) 22 | } 23 | case SYSL: 24 | result := strings.ToLower(inst.String()) 25 | return strings.Replace(result, "c", "C", -1) 26 | case DCPS1, DCPS2, DCPS3, CLREX: 27 | return strings.ToLower(strings.TrimSpace(inst.String())) 28 | case ISB: 29 | if strings.Contains(inst.String(), "SY") { 30 | result := strings.TrimSuffix(inst.String(), " SY") 31 | return strings.ToLower(result) 32 | } 33 | } 34 | return strings.ToLower(inst.String()) 35 | } 36 | -------------------------------------------------------------------------------- /arm64/arm64asm/objdump_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package arm64asm 6 | 7 | import ( 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestObjdumpARM64TestDecodeGNUSyntaxdata(t *testing.T) { 13 | testObjdumpARM64(t, testdataCases(t, "gnu")) 14 | } 15 | func TestObjdumpARM64TestDecodeGoSyntaxdata(t *testing.T) { 16 | testObjdumpARM64(t, testdataCases(t, "plan9")) 17 | } 18 | func TestObjdumpARM64Manual(t *testing.T) { testObjdumpARM64(t, hexCases(t, objdumpManualTests)) } 19 | func TestObjdumpARM64Cond(t *testing.T) { testObjdumpARM64(t, condCases(t)) } 20 | func TestObjdumpARM64(t *testing.T) { testObjdumpARM64(t, JSONCases(t)) } 21 | 22 | // objdumpManualTests holds test cases that will be run by TestObjdumpARMManual. 23 | // If you are debugging a few cases that turned up in a longer run, it can be useful 24 | // to list them here and then use -run=Manual, particularly with tracing enabled. 25 | // Note that these are byte sequences, so they must be reversed from the usual 26 | // word presentation. 27 | var objdumpManualTests = ` 28 | bf2003d5 29 | 9f2003d5 30 | 7f2003d5 31 | 5f2003d5 32 | 3f2003d5 33 | 1f2003d5 34 | df4d03d5 35 | ff4d03d5 36 | 28d91b14 37 | da6cb530 38 | 15e5e514 39 | ff4603d5 40 | df4803d5 41 | bf4100d5 42 | 9f3f03d5 43 | 9f3e03d5 44 | 9f3d03d5 45 | 9f3b03d5 46 | 9f3a03d5 47 | 9f3903d5 48 | 9f3703d5 49 | 9f3603d5 50 | 9f3503d5 51 | 9f3303d5 52 | 9f3203d5 53 | 9f3103d5 54 | ff4603d5 55 | df4803d5 56 | bf4100d5 57 | a3681b53 58 | 47dc78d3 59 | 0500a012 60 | 0500e092 61 | 0500a052 62 | 0500a0d2 63 | cd5a206e 64 | cd5a202e 65 | 743d050e 66 | 743d0a0e 67 | 743d0c0e 68 | 743d084e 69 | ` 70 | 71 | // allowedMismatchObjdump reports whether the mismatch between text and dec 72 | // should be allowed by the test. 73 | func allowedMismatchObjdump(text string, inst *Inst, dec ExtInst) bool { 74 | // Skip unsupported instructions 75 | if hasPrefix(dec.text, todo...) { 76 | return true 77 | } 78 | // GNU objdump has incorrect alias conditions for following instructions 79 | if inst.Enc&0x000003ff == 0x000003ff && hasPrefix(dec.text, "negs") && hasPrefix(text, "cmp") { 80 | return true 81 | } 82 | // GNU objdump "NV" is equal to our "AL" 83 | if strings.HasSuffix(dec.text, " nv") && strings.HasSuffix(text, " al") { 84 | return true 85 | } 86 | if strings.HasPrefix(dec.text, "b.nv") && strings.HasPrefix(text, "b.al") { 87 | return true 88 | } 89 | // GNU objdump recognizes invalid binaries as following instructions 90 | if hasPrefix(dec.text, "hint", "mrs", "msr", "bfc", "orr", "mov") { 91 | return true 92 | } 93 | if strings.HasPrefix(text, "hint") { 94 | return true 95 | } 96 | // GNU objdump recognizes reserved valuse as valid ones 97 | if strings.Contains(text, "unknown instruction") && hasPrefix(dec.text, "fmla", "fmul", "fmulx", "fcvtzs", "fcvtzu", "fmls", "fmov", "scvtf", "ucvtf") { 98 | return true 99 | } 100 | // Some old objdump recognizes ldur*/stur*/prfum as ldr*/str*/prfm 101 | for k, v := range oldObjdumpMismatch { 102 | if strings.HasPrefix(dec.text, k) && strings.Replace(dec.text, k, v, 1) == text { 103 | return true 104 | } 105 | } 106 | // New objdump supports some newer mnemonics than this package. This 107 | // package should be updated to support the new mnemonics and the sense 108 | // of this reversed to continue passing with older objdumps but that 109 | // requires internal ARM tooling. 110 | if newForm, ok := newMnemonics[text]; ok && newForm == dec.text { 111 | return true 112 | } 113 | // GNU objdump misses spaces between operands for some instructions (e.g., "ld1 {v10.2s, v11.2s}, [x23],#16") 114 | if strings.Replace(text, " ", "", -1) == strings.Replace(dec.text, " ", "", -1) { 115 | return true 116 | } 117 | return false 118 | } 119 | 120 | // TODO: system instruction. 121 | var todo = strings.Fields(` 122 | sys 123 | at 124 | ic 125 | hvc 126 | smc 127 | `) 128 | 129 | // Following instructions can't be covered because they are just aliases to another instructions which are always preferred 130 | var Ncover = strings.Fields(` 131 | sbfm 132 | asrv 133 | bfm 134 | ubfm 135 | lslv 136 | lsrv 137 | rorv 138 | ins 139 | dup 140 | `) 141 | 142 | // Some old objdump wrongly decodes following instructions and allow their mismatches to avoid false alarm 143 | var oldObjdumpMismatch = map[string]string{ 144 | //oldObjValue correctValue 145 | "ldr": "ldur", 146 | "ldrb": "ldurb", 147 | "ldrh": "ldurh", 148 | "ldrsb": "ldursb", 149 | "ldrsh": "ldursh", 150 | "ldrsw": "ldursw", 151 | "str": "stur", 152 | "strb": "sturb", 153 | "strh": "sturh", 154 | "prfm": "prfum", 155 | } 156 | 157 | var newMnemonics = map[string]string{ 158 | "dsb #0x00": "ssbb", 159 | "dsb #0x04": "pssbb", 160 | } 161 | -------------------------------------------------------------------------------- /arm64/arm64asm/testdata/Makefile: -------------------------------------------------------------------------------- 1 | go test command: 2 | cd ..; go test -run 'ObjdumpARM64Cond' -v -timeout 10h -long 2>&1 | tee log 3 | cd ..; go test -run 'ObjdumpARM64TestGUNSyntaxdata' -v -timeout 10h -long 2>&1 | tee -a log 4 | cd ..; go test -run 'ObjdumpARM64TestGoSyntaxdata' -v -timeout 10h -long 2>&1 | tee -a log 5 | cd ..; go test -run 'ObjdumpARM64' -v -timeout 10h -long 2>&1 | tee -a log 6 | cd ..; go test -run 'ObjdumpARM64Manual' -v -timeout 10h -long 2>&1 | tee -a log 7 | cd ..; go test -run 'TestDecodeGNUSyntax' 8 | cd ..; go test -run 'TestDecodeGoSyntax' 9 | cd ..; go test -run '.*' 10 | -------------------------------------------------------------------------------- /codereview.cfg: -------------------------------------------------------------------------------- 1 | issuerepo: golang/go 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module golang.org/x/arch 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | gopkg.in/yaml.v3 v3.0.1 7 | rsc.io/pdf v0.1.1 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 2 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 3 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 4 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 5 | rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= 6 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 7 | -------------------------------------------------------------------------------- /internal/simdgen/.gitignore: -------------------------------------------------------------------------------- 1 | testdata/* 2 | -------------------------------------------------------------------------------- /internal/simdgen/asm.yaml.toy: -------------------------------------------------------------------------------- 1 | # Hand-written toy input like -xedPath would generate. 2 | # This input can be substituted for -xedPath. 3 | !sum 4 | - asm: ADDPS 5 | goarch: amd64 6 | feature: "SSE2" 7 | in: 8 | - asmPos: 0 9 | class: vreg 10 | base: float 11 | elemBits: 32 12 | bits: 128 13 | - asmPos: 1 14 | class: vreg 15 | base: float 16 | elemBits: 32 17 | bits: 128 18 | out: 19 | - asmPos: 0 20 | class: vreg 21 | base: float 22 | elemBits: 32 23 | bits: 128 24 | 25 | - asm: ADDPD 26 | goarch: amd64 27 | feature: "SSE2" 28 | in: 29 | - asmPos: 0 30 | class: vreg 31 | base: float 32 | elemBits: 64 33 | bits: 128 34 | - asmPos: 1 35 | class: vreg 36 | base: float 37 | elemBits: 64 38 | bits: 128 39 | out: 40 | - asmPos: 0 41 | class: vreg 42 | base: float 43 | elemBits: 64 44 | bits: 128 45 | 46 | - asm: PADDB 47 | goarch: amd64 48 | feature: "SSE2" 49 | in: 50 | - asmPos: 0 51 | class: vreg 52 | base: int|uint 53 | elemBits: 32 54 | bits: 128 55 | - asmPos: 1 56 | class: vreg 57 | base: int|uint 58 | elemBits: 32 59 | bits: 128 60 | out: 61 | - asmPos: 0 62 | class: vreg 63 | base: int|uint 64 | elemBits: 32 65 | bits: 128 66 | 67 | - asm: VPADDB 68 | goarch: amd64 69 | feature: "AVX" 70 | in: 71 | - asmPos: 1 72 | class: vreg 73 | base: int|uint 74 | elemBits: 8 75 | bits: 128 76 | - asmPos: 2 77 | class: vreg 78 | base: int|uint 79 | elemBits: 8 80 | bits: 128 81 | out: 82 | - asmPos: 0 83 | class: vreg 84 | base: int|uint 85 | elemBits: 8 86 | bits: 128 87 | 88 | - asm: VPADDB 89 | goarch: amd64 90 | feature: "AVX2" 91 | in: 92 | - asmPos: 1 93 | class: vreg 94 | base: int|uint 95 | elemBits: 8 96 | bits: 256 97 | - asmPos: 2 98 | class: vreg 99 | base: int|uint 100 | elemBits: 8 101 | bits: 256 102 | out: 103 | - asmPos: 0 104 | class: vreg 105 | base: int|uint 106 | elemBits: 8 107 | bits: 256 108 | -------------------------------------------------------------------------------- /internal/simdgen/etetest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | cat <<\\EOF 4 | 5 | This is an end-to-end test of Go SIMD. It checks out a fresh Go 6 | repository from the go.simd branch, then generates the SIMD input 7 | files and runs simdgen writing into the fresh repository. 8 | 9 | After that it generates the modified ssa pattern matching files, then 10 | builds the compiler. 11 | 12 | \EOF 13 | 14 | rm -rf go-test 15 | git clone https://go.googlesource.com/go -b dev.simd go-test 16 | go generate 17 | go run . -xedPath xeddata -o godefs -goroot ./go-test go.yaml types.yaml categories.yaml 18 | (cd go-test/src/cmd/compile/internal/ssa/_gen ; go run *.go ) 19 | (cd go-test/src ; GOEXPERIMENT=simd ./make.bash ) 20 | (cd go-test/bin; b=`pwd` ; cd ../src/simd/testdata; GOARCH=amd64 $b/go run .) 21 | # next, add some tests of SIMD itself 22 | -------------------------------------------------------------------------------- /internal/simdgen/gen_simdGenericOps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "sort" 10 | ) 11 | 12 | const simdGenericOpsTmpl = `// Code generated by x/arch/internal/simdgen using 'go run . -xedPath $XED_PATH -o godefs -goroot $GOROOT go.yaml types.yaml categories.yaml'; DO NOT EDIT. 13 | package main 14 | 15 | func simdGenericOps() []opData { 16 | return []opData{ 17 | {{- range . }} 18 | {name: "{{.OpName}}", argLength: {{.OpInLen}}, commutative: {{.Comm}}}, 19 | {{- end }} 20 | } 21 | } 22 | ` 23 | 24 | // writeSIMDGenericOps generates the generic ops and writes it to simdAMD64ops.go 25 | // within the specified directory. 26 | func writeSIMDGenericOps(directory string, ops []Operation) error { 27 | file, t, err := openFileAndPrepareTemplate(directory, "src/cmd/compile/internal/ssa/_gen/simdgenericOps.go", simdGenericOpsTmpl) 28 | if err != nil { 29 | return err 30 | } 31 | defer file.Close() 32 | type genericOpsData struct { 33 | sortKey string 34 | OpName string 35 | OpInLen int 36 | Comm string 37 | } 38 | opsData := make([]genericOpsData, 0) 39 | for _, op := range ops { 40 | _, _, _, _, _, gOp, err := op.shape() 41 | if err != nil { 42 | return err 43 | } 44 | genericNames := gOp.Go + *gOp.In[0].Go 45 | opsData = append(opsData, genericOpsData{*gOp.In[0].Go + gOp.Go, genericNames, len(gOp.In), op.Commutative}) 46 | } 47 | sort.Slice(opsData, func(i, j int) bool { 48 | return opsData[i].sortKey < opsData[j].sortKey 49 | }) 50 | 51 | err = t.Execute(file, opsData) 52 | if err != nil { 53 | return fmt.Errorf("failed to execute template: %w", err) 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /internal/simdgen/gen_simdIntrinsics.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const simdIntrinsicsTmpl = `// Code generated by x/arch/internal/simdgen using 'go run . -xedPath $XED_PATH -o godefs -goroot $GOROOT go.yaml types.yaml categories.yaml'; DO NOT EDIT. 12 | package ssagen 13 | 14 | import ( 15 | "cmd/compile/internal/ir" 16 | "cmd/compile/internal/ssa" 17 | "cmd/compile/internal/types" 18 | "cmd/internal/sys" 19 | ) 20 | 21 | const simdPackage = "` + simdPackage + `" 22 | 23 | func simdIntrinsics(addF func(pkg, fn string, b intrinsicBuilder, archFamilies ...sys.ArchFamily)) { 24 | {{- range .OpsLen1}} 25 | addF(simdPackage, "{{(index .In 0).Go}}.{{.Go}}", opLen1(ssa.Op{{.Go}}{{(index .In 0).Go}}, {{.GoArch}}), sys.AMD64) 26 | {{- end}} 27 | {{- range .OpsLen2}} 28 | addF(simdPackage, "{{(index .In 0).Go}}.{{.Go}}", opLen2(ssa.Op{{.Go}}{{(index .In 0).Go}}, {{.GoArch}}), sys.AMD64) 29 | {{- end}} 30 | {{- range .OpsLen3}} 31 | addF(simdPackage, "{{(index .In 0).Go}}.{{.Go}}", opLen3(ssa.Op{{.Go}}{{(index .In 0).Go}}, {{.GoArch}}), sys.AMD64) 32 | {{- end}} 33 | 34 | {{- range .VectorConversions }} 35 | addF(simdPackage, "{{.Tsrc.Name}}.As{{.Tdst.Name}}", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return args[0] }, sys.AMD64) 36 | {{- end}} 37 | 38 | {{- range $size, $ts := .TypeMap }} 39 | {{- range $t := $ts }} 40 | addF(simdPackage, "Load{{$t.Name}}", simdLoad(), sys.AMD64) 41 | addF(simdPackage, "{{$t.Name}}.Store", simdStore(), sys.AMD64) 42 | {{- end}} 43 | {{- end}} 44 | {{- range .Masks }} 45 | addF(simdPackage, "{{.Name}}.As{{.VectorCounterpart}}", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return args[0] }, sys.AMD64) 46 | addF(simdPackage, "{{.VectorCounterpart}}.As{{.Name}}", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return args[0] }, sys.AMD64) 47 | addF(simdPackage, "{{.Name}}.And", opLen2(ssa.OpAnd{{.ReshapedVectorWithAndOr}}, types.TypeVec{{.Size}}), sys.AMD64) 48 | addF(simdPackage, "{{.Name}}.Or", opLen2(ssa.OpOr{{.ReshapedVectorWithAndOr}}, types.TypeVec{{.Size}}), sys.AMD64) 49 | {{- end}} 50 | } 51 | 52 | func opLen1(op ssa.Op, t *types.Type) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 53 | return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 54 | return s.newValue1(op, t, args[0]) 55 | } 56 | } 57 | 58 | func opLen2(op ssa.Op, t *types.Type) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 59 | return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 60 | return s.newValue2(op, t, args[0], args[1]) 61 | } 62 | } 63 | 64 | func opLen3(op ssa.Op, t *types.Type) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 65 | return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 66 | return s.newValue3(op, t, args[0], args[1], args[2]) 67 | } 68 | } 69 | 70 | func simdLoad() func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 71 | return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 72 | return s.newValue2(ssa.OpLoad, n.Type(), args[0], s.mem()) 73 | } 74 | } 75 | 76 | func simdStore() func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 77 | return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { 78 | s.store(args[0].Type, args[1], args[0]) 79 | return nil 80 | } 81 | } 82 | ` 83 | 84 | // writeSIMDIntrinsics generates the intrinsic mappings and writes it to simdintrinsics.go 85 | // within the specified directory. 86 | func writeSIMDIntrinsics(directory string, ops []Operation, typeMap simdTypeMap) error { 87 | file, t, err := openFileAndPrepareTemplate(directory, "src/cmd/compile/internal/ssagen/simdintrinsics.go", simdIntrinsicsTmpl) 88 | if err != nil { 89 | return err 90 | } 91 | defer file.Close() 92 | opsLen1, opsLen2, opsLen3, err := genericOpsByLen(ops) 93 | if err != nil { 94 | return err 95 | } 96 | 97 | type templateData struct { 98 | OpsLen1 []Operation 99 | OpsLen2 []Operation 100 | OpsLen3 []Operation 101 | TypeMap simdTypeMap 102 | VectorConversions []simdTypePair 103 | Masks []simdType 104 | } 105 | err = t.Execute(file, templateData{opsLen1, opsLen2, opsLen3, typeMap, vConvertFromTypeMap(typeMap), masksFromTypeMap(typeMap)}) 106 | if err != nil { 107 | return fmt.Errorf("failed to execute template: %w", err) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /internal/simdgen/gen_simdMachineOps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "sort" 10 | ) 11 | 12 | const simdMachineOpsTmpl = `// Code generated by x/arch/internal/simdgen using 'go run . -xedPath $XED_PATH -o godefs -goroot $GOROOT go.yaml types.yaml categories.yaml'; DO NOT EDIT. 13 | package main 14 | 15 | func simdAMD64Ops(fp1fp1, fp2fp1, fp2m1, fp1m1fp1, fp2m1fp1, fp2m1m1 regInfo) []opData { 16 | return []opData{ 17 | {{- range .OpsData }} 18 | {name: "{{.OpName}}", argLength: {{.OpInLen}}, reg: {{.RegInfo}}, asm: "{{.Asm}}", commutative: {{.Comm}}, typ: "{{.Type}}"}, 19 | {{- end }} 20 | {{- range .OpsDataImm }} 21 | {name: "{{.OpName}}", argLength: {{.OpInLen}}, reg: {{.RegInfo}}, asm: "{{.Asm}}", aux: "Int8", commutative: {{.Comm}}, typ: "{{.Type}}"}, 22 | {{- end }} 23 | } 24 | } 25 | ` 26 | 27 | // writeSIMDMachineOps generates the machine ops and writes it to simdAMD64ops.go 28 | // within the specified directory. 29 | func writeSIMDMachineOps(directory string, ops []Operation) error { 30 | file, t, err := openFileAndPrepareTemplate(directory, "src/cmd/compile/internal/ssa/_gen/simdAMD64ops.go", simdMachineOpsTmpl) 31 | if err != nil { 32 | return err 33 | } 34 | defer file.Close() 35 | type opData struct { 36 | sortKey string 37 | OpName string 38 | Asm string 39 | OpInLen int 40 | RegInfo string 41 | Comm string 42 | Type string 43 | } 44 | type machineOpsData struct { 45 | OpsData []opData 46 | OpsDataImm []opData 47 | } 48 | seen := map[string]struct{}{} 49 | regInfoSet := map[string]bool{"fp1fp1": true, "fp2fp1": true, "fp2m1": true, "fp2m1fp1": true, "fp2m1m1": true, "fp1m1fp1": true} 50 | opsData := make([]opData, 0) 51 | opsDataImm := make([]opData, 0) 52 | for _, op := range ops { 53 | shapeIn, shapeOut, maskType, _, _, gOp, err := op.shape() 54 | if err != nil { 55 | return err 56 | } 57 | asm := gOp.Asm 58 | if maskType == OneMask { 59 | asm += "Masked" 60 | } 61 | asm = fmt.Sprintf("%s%d", asm, *gOp.Out[0].Bits) 62 | // TODO: all our masked operations are now zeroing, we need to generate machine ops with merging masks, maybe copy 63 | // one here with a name suffix "Merging". The rewrite rules will need them. 64 | if _, ok := seen[asm]; ok { 65 | continue 66 | } 67 | seen[asm] = struct{}{} 68 | var regInfo string 69 | // Process input reg shapes. 70 | var vRegInCnt, kMaskInCnt, vRegOutCnt, kMaskOutCnt int 71 | for _, in := range gOp.In { 72 | if in.Class == "vreg" { 73 | vRegInCnt++ 74 | } else if in.Class == "mask" { 75 | kMaskInCnt++ 76 | } 77 | } 78 | for _, out := range gOp.Out { 79 | // If class overwrite is happening, that's not really a mask but a vreg. 80 | if out.Class == "vreg" || out.OverwriteClass != nil { 81 | vRegOutCnt++ 82 | } else if out.Class == "mask" { 83 | kMaskOutCnt++ 84 | } 85 | } 86 | var vRegInS, kMaskInS, vRegOutS, kMaskOutS string 87 | if vRegInCnt > 0 { 88 | vRegInS = fmt.Sprintf("fp%d", vRegInCnt) 89 | } 90 | if kMaskInCnt > 0 { 91 | kMaskInS = fmt.Sprintf("m%d", kMaskInCnt) 92 | } 93 | if vRegOutCnt > 0 { 94 | vRegOutS = fmt.Sprintf("fp%d", vRegOutCnt) 95 | } 96 | if kMaskOutCnt > 0 { 97 | kMaskOutS = fmt.Sprintf("m%d", kMaskOutCnt) 98 | } 99 | regInfo = fmt.Sprintf("%s%s%s%s", vRegInS, kMaskInS, vRegOutS, kMaskOutS) 100 | if _, ok := regInfoSet[regInfo]; !ok { 101 | return fmt.Errorf("unsupported register constraint, please update the template and AMD64Ops.go: %s", regInfo) 102 | } 103 | var outType string 104 | if shapeOut == OneVregOut || gOp.Out[0].OverwriteClass != nil { 105 | // If class overwrite is happening, that's not really a mask but a vreg. 106 | outType = fmt.Sprintf("Vec%d", *gOp.Out[0].Bits) 107 | } else if shapeOut == OneKmaskOut { 108 | outType = "Mask" 109 | } else { 110 | return fmt.Errorf("simdgen does not recognize this output shape: %d", shapeOut) 111 | } 112 | if shapeIn == OneConstImmIn || shapeIn == OneKmaskConstImmIn { 113 | opsDataImm = append(opsDataImm, opData{*gOp.In[0].Go + gOp.Go, asm, gOp.Asm, len(gOp.In), regInfo, gOp.Commutative, outType}) 114 | } else { 115 | opsData = append(opsData, opData{*gOp.In[0].Go + gOp.Go, asm, gOp.Asm, len(gOp.In), regInfo, gOp.Commutative, outType}) 116 | } 117 | } 118 | sort.Slice(opsData, func(i, j int) bool { 119 | return opsData[i].sortKey < opsData[j].sortKey 120 | }) 121 | sort.Slice(opsDataImm, func(i, j int) bool { 122 | return opsDataImm[i].sortKey < opsDataImm[j].sortKey 123 | }) 124 | err = t.Execute(file, machineOpsData{opsData, opsDataImm}) 125 | if err != nil { 126 | return fmt.Errorf("failed to execute template: %w", err) 127 | } 128 | 129 | return nil 130 | } 131 | -------------------------------------------------------------------------------- /internal/simdgen/gen_simdssa.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | const simdssaTmpl = `// Code generated by x/arch/internal/simdgen using 'go run . -xedPath $XED_PATH -o godefs -goroot $GOROOT go.yaml types.yaml categories.yaml'; DO NOT EDIT. 13 | 14 | package amd64 15 | 16 | import ( 17 | "cmd/compile/internal/ssa" 18 | "cmd/compile/internal/ssagen" 19 | "cmd/internal/obj" 20 | "cmd/internal/obj/x86" 21 | ) 22 | 23 | func ssaGenSIMDValue(s *ssagen.State, v *ssa.Value) bool { 24 | p := s.Prog(v.Op.Asm()) 25 | // First arg 26 | switch v.Op {{"{"}}{{if gt (len .Imms) 0}} 27 | // Immediates 28 | case {{.Imms}}: 29 | imm := v.AuxInt 30 | if imm < 0 || imm > 255 { 31 | v.Fatalf("Invalid source selection immediate") 32 | } 33 | p.From.Offset = imm 34 | p.From.Type = obj.TYPE_CONST 35 | {{end}}{{if gt (len .Reg0) 0}} 36 | // Registers 37 | case {{.Reg0}}: 38 | p.From.Type = obj.TYPE_REG 39 | p.From.Reg = simdReg(v.Args[0]) 40 | {{end}} 41 | default: 42 | // At least one arg is required. 43 | return false 44 | } 45 | 46 | // Second arg 47 | switch v.Op {{"{"}}{{if gt (len .Reg1) 0}} 48 | // Registers 49 | case {{.Reg1}}: 50 | if p.From.Type == obj.TYPE_CONST { 51 | p.AddRestSourceReg(simdReg(v.Args[0])) 52 | } else { 53 | p.AddRestSourceReg(simdReg(v.Args[1])) 54 | }{{end}} 55 | } 56 | 57 | // Third arg 58 | switch v.Op {{"{"}}{{if gt (len .Reg2) 0}} 59 | // Registers 60 | case {{.Reg2}}: 61 | if p.From.Type == obj.TYPE_CONST { 62 | p.AddRestSourceReg(simdReg(v.Args[1])) 63 | } else { 64 | p.AddRestSourceReg(simdReg(v.Args[2])) 65 | }{{end}} 66 | } 67 | 68 | // Fourth arg 69 | switch v.Op {{"{"}}{{if gt (len .Reg3) 0}} 70 | case {{.Reg3}}: 71 | if p.From.Type == obj.TYPE_CONST { 72 | p.AddRestSourceReg(simdReg(v.Args[2])) 73 | } else { 74 | p.AddRestSourceReg(simdReg(v.Args[3])) 75 | }{{end}} 76 | } 77 | 78 | // Output 79 | switch v.Op {{"{"}}{{if gt (len .All) 0}} 80 | case {{.All}}: 81 | p.To.Type = obj.TYPE_REG 82 | p.To.Reg = simdReg(v) 83 | {{end}} 84 | default: 85 | // One result is required. 86 | return false 87 | } 88 | {{if gt (len .ZeroingMask) 0}} 89 | // Masked operation are always compiled with zeroing. 90 | switch v.Op { 91 | case {{.ZeroingMask}}: 92 | x86.ParseSuffix(p, "Z") 93 | } 94 | {{end}} 95 | return true 96 | } 97 | ` 98 | 99 | // writeSIMDSSA generates the ssa to prog lowering codes and writes it to simdssa.go 100 | // within the specified directory. 101 | func writeSIMDSSA(directory string, ops []Operation) error { 102 | var Imms []string 103 | var All []string 104 | var ZeroingMask []string 105 | Regs := map[int][]string{} 106 | 107 | seen := map[string]struct{}{} 108 | for _, op := range ops { 109 | asm := op.Asm 110 | shapeIn, _, maskType, _, _, gOp, err := op.shape() 111 | if err != nil { 112 | return err 113 | } 114 | if maskType == 2 { 115 | asm += "Masked" 116 | } 117 | asm = fmt.Sprintf("%s%d", asm, *gOp.Out[0].Bits) 118 | if _, ok := seen[asm]; ok { 119 | continue 120 | } 121 | seen[asm] = struct{}{} 122 | caseStr := fmt.Sprintf("ssa.OpAMD64%s", asm) 123 | if shapeIn == OneKmaskIn || shapeIn == OneKmaskConstImmIn { 124 | if gOp.Zeroing == nil { 125 | ZeroingMask = append(ZeroingMask, caseStr) 126 | } 127 | } 128 | immCount := 0 129 | if shapeIn == OneConstImmIn || shapeIn == OneKmaskConstImmIn { 130 | immCount++ 131 | Imms = append(Imms, caseStr) 132 | } 133 | for i := range len(gOp.In) { 134 | if i > 2 { 135 | return fmt.Errorf("simdgen does not recognize more than 3 registers: %s", gOp) 136 | } 137 | Regs[i+immCount] = append(Regs[i+immCount], caseStr) 138 | } 139 | All = append(All, caseStr) 140 | } 141 | 142 | data := struct { 143 | Imms string 144 | Reg0 string 145 | Reg1 string 146 | Reg2 string 147 | Reg3 string 148 | All string 149 | ZeroingMask string 150 | }{ 151 | strings.Join(Imms, ", "), 152 | strings.Join(Regs[0], ", "), 153 | strings.Join(Regs[1], ", "), 154 | strings.Join(Regs[2], ", "), 155 | strings.Join(Regs[3], ", "), 156 | strings.Join(All, ", "), 157 | strings.Join(ZeroingMask, ", "), 158 | } 159 | 160 | file, t, err := openFileAndPrepareTemplate(directory, "src/cmd/compile/internal/amd64/simdssa.go", simdssaTmpl) 161 | if err != nil { 162 | return err 163 | } 164 | defer file.Close() 165 | 166 | err = t.Execute(file, data) 167 | if err != nil { 168 | return fmt.Errorf("failed to execute template: %w", err) 169 | } 170 | 171 | return nil 172 | } 173 | -------------------------------------------------------------------------------- /internal/simdgen/godefs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "log" 9 | 10 | "golang.org/x/arch/internal/unify" 11 | ) 12 | 13 | type Operation struct { 14 | Go string // Go method name 15 | 16 | GoArch string // GOARCH for this definition 17 | Asm string // Assembly mnemonic 18 | 19 | In []Operand // Arguments 20 | Out []Operand // Results 21 | Commutative string // Commutativity 22 | Extension string // Extension 23 | Zeroing *string // Zeroing is a flag for asm prefix "Z", if non-nil it will always be "false" 24 | Documentation *string // Documentation will be appended to the stubs comments. 25 | // ConstMask is a hack to reduce the size of defs the user writes for const-immediate 26 | // If present, it will be copied to [In[0].Const]. 27 | ConstImm *string 28 | // Masked indicates that this is a masked operation, this field has to be set for masked operations 29 | // otherwise simdgen won't recognize it in [splitMask]. 30 | Masked *string 31 | } 32 | 33 | type Operand struct { 34 | Class string // One of "mask", "immediate", "vreg" and "mem" 35 | 36 | Go *string // Go type of this operand 37 | AsmPos int // Position of this operand in the assembly instruction 38 | 39 | Base *string // Base Go type ("int", "uint", "float") 40 | ElemBits *int // Element bit width 41 | Bits *int // Total vector bit width 42 | 43 | Const *string // Optional constant value 44 | Lanes *int // Lanes should equal Bits/ElemBits 45 | // If non-nil, it means the [Class] field is overwritten here, right now this is used to 46 | // overwrite the results of AVX2 compares to masks. 47 | OverwriteClass *string 48 | // If non-nil, it means the [Base] field is overwritten here. This field exist solely 49 | // because Intel's XED data is inconsistent. e.g. VANDNP[SD] marks its operand int. 50 | OverwriteBase *string 51 | } 52 | 53 | func writeGoDefs(path string, cl unify.Closure) error { 54 | // TODO: Merge operations with the same signature but multiple 55 | // implementations (e.g., SSE vs AVX) 56 | var ops []Operation 57 | for def := range cl.All() { 58 | var op Operation 59 | if !def.Exact() { 60 | continue 61 | } 62 | if err := def.Decode(&op); err != nil { 63 | log.Println(err.Error()) 64 | log.Println(def) 65 | continue 66 | } 67 | // TODO: verify that this is safe. 68 | op.sortOperand() 69 | ops = append(ops, op) 70 | } 71 | // The parsed XED data might contain duplicates, like 72 | // 512 bits VPADDP. 73 | deduped := dedup(ops) 74 | log.Printf("dedup len: %d\n", len(ops)) 75 | var err error 76 | if err = overwrite(deduped); err != nil { 77 | return err 78 | } 79 | log.Printf("dedup len: %d\n", len(deduped)) 80 | if !*FlagNoSplitMask { 81 | if deduped, err = splitMask(deduped); err != nil { 82 | return err 83 | } 84 | } 85 | log.Printf("dedup len: %d\n", len(deduped)) 86 | if !*FlagNoDedup { 87 | if deduped, err = dedupGodef(deduped); err != nil { 88 | return err 89 | } 90 | } 91 | log.Printf("dedup len: %d\n", len(deduped)) 92 | if !*FlagNoConstImmPorting { 93 | if err = copyConstImm(deduped); err != nil { 94 | return err 95 | } 96 | } 97 | log.Printf("dedup len: %d\n", len(deduped)) 98 | typeMap := parseSIMDTypes(deduped) 99 | if err = writeSIMDTypes(path, typeMap); err != nil { 100 | return err 101 | } 102 | if err = writeSIMDStubs(path, deduped, typeMap); err != nil { 103 | return err 104 | } 105 | if err = writeSIMDIntrinsics(path, deduped, typeMap); err != nil { 106 | return err 107 | } 108 | if err = writeSIMDGenericOps(path, deduped); err != nil { 109 | return err 110 | } 111 | if err = writeSIMDMachineOps(path, deduped); err != nil { 112 | return err 113 | } 114 | if err = writeSIMDRules(path, deduped); err != nil { 115 | return err 116 | } 117 | if err = writeSIMDSSA(path, deduped); err != nil { 118 | return err 119 | } 120 | return nil 121 | } 122 | -------------------------------------------------------------------------------- /internal/simdgen/ops/AddSub/categories.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | - go: Add 3 | commutative: "true" 4 | extension: "AVX.*" 5 | - go: SaturatedAdd 6 | commutative: "true" 7 | extension: "AVX.*" 8 | - go: MaskedAdd 9 | masked: "true" 10 | commutative: "true" 11 | extension: "AVX.*" 12 | - go: MaskedSaturatedAdd 13 | masked: "true" 14 | commutative: "true" 15 | extension: "AVX.*" 16 | - go: Sub 17 | commutative: "true" 18 | extension: "AVX.*" 19 | - go: SaturatedSub 20 | commutative: "true" 21 | extension: "AVX.*" 22 | - go: MaskedSub 23 | masked: "true" 24 | commutative: "true" 25 | extension: "AVX.*" 26 | - go: MaskedSaturatedSub 27 | masked: "true" 28 | commutative: "true" 29 | extension: "AVX.*" -------------------------------------------------------------------------------- /internal/simdgen/ops/AddSub/go.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | # Add 3 | - go: Add 4 | asm: "VPADD[BWDQ]|VADDP[SD]" 5 | in: 6 | - &any 7 | go: $t 8 | - *any 9 | out: 10 | - *any 11 | - go: MaskedAdd 12 | asm: "VPADD[BWDQ]|VADDP[SD]" 13 | in: 14 | - class: mask 15 | - *any 16 | - *any 17 | out: 18 | - *any 19 | # Saturated Add 20 | - go: SaturatedAdd 21 | asm: "VPADDS[BWDQ]" 22 | in: 23 | - &int 24 | go: $t 25 | base: int 26 | - *int 27 | out: 28 | - *int 29 | - go: SaturatedAdd 30 | asm: "VPADDS[BWDQ]" 31 | in: 32 | - &uint 33 | go: $t 34 | base: uint 35 | - *uint 36 | out: 37 | - *uint 38 | - go: MaskedSaturatedAdd 39 | asm: "VPADDS[BWDQ]" 40 | in: 41 | - class: mask 42 | - *int 43 | - *int 44 | out: 45 | - *int 46 | - go: MaskedSaturatedAdd 47 | asm: "VPADDS[BWDQ]" 48 | in: 49 | - class: mask 50 | - *uint 51 | - *uint 52 | out: 53 | - *uint 54 | 55 | # Sub 56 | - go: Sub 57 | asm: "VPSUB[BWDQ]|VADDP[SD]" 58 | in: 59 | - *any 60 | - *any 61 | out: 62 | - *any 63 | - go: MaskedSub 64 | asm: "VPSUB[BWDQ]|VADDP[SD]" 65 | in: 66 | - class: mask 67 | - *any 68 | - *any 69 | out: 70 | - *any 71 | # Saturated Sub 72 | - go: SaturatedSub 73 | asm: "VPSUBS[BWDQ]" 74 | in: 75 | - *int 76 | - *int 77 | out: 78 | - *int 79 | - go: SaturatedSub 80 | asm: "VPSUBS[BWDQ]" 81 | in: 82 | - *uint 83 | - *uint 84 | out: 85 | - *uint 86 | - go: MaskedSaturatedSub 87 | asm: "VPSUBS[BWDQ]" 88 | in: 89 | - class: mask 90 | - *int 91 | - *int 92 | out: 93 | - *int 94 | - go: MaskedSaturatedSub 95 | asm: "VPSUBS[BWDQ]" 96 | in: 97 | - class: mask 98 | - *uint 99 | - *uint 100 | out: 101 | - *uint 102 | -------------------------------------------------------------------------------- /internal/simdgen/ops/BitwiseLogic/categories.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | - go: And 3 | commutative: "true" 4 | extension: "AVX.*" 5 | - go: MaskedAnd 6 | masked: "true" 7 | commutative: "true" 8 | extension: "AVX.*" 9 | - go: Or 10 | commutative: "true" 11 | extension: "AVX.*" 12 | - go: MaskedOr 13 | masked: "true" 14 | commutative: "true" 15 | extension: "AVX.*" 16 | - go: AndNot 17 | commutative: "true" 18 | extension: "AVX.*" 19 | - go: MaskedAndNot 20 | masked: "true" 21 | commutative: "true" 22 | extension: "AVX.*" 23 | - go: Xor 24 | commutative: "true" 25 | extension: "AVX.*" 26 | - go: MaskedXor 27 | masked: "true" 28 | commutative: "true" 29 | extension: "AVX.*" 30 | # We also have PTEST and VPTERNLOG, those should be hidden from the users 31 | # and only appear in rewrite rules. -------------------------------------------------------------------------------- /internal/simdgen/ops/BitwiseLogic/go.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | # In the XED data, *all* floating point bitwise logic operation has their 3 | # operand type marked as uint. We are not trying to understand why Intel 4 | # decided that they want FP bit-wise logic operations, but this irregularity 5 | # has to be dealed with in separate rules with some overwrites. 6 | 7 | # Int/Uint operations. 8 | # Non-masked for 128/256-bit vectors 9 | # For binary operations, we constrain their two inputs and one output to the 10 | # same Go type using a variable. This will map to instructions before AVX512. 11 | - go: And 12 | asm: "VPAND" 13 | in: 14 | - &any 15 | go: $t 16 | - *any 17 | out: 18 | - *any 19 | # Masked 20 | # Looks like VPAND$xi works only for 2 shapes for integer: 21 | # Dword and Qword. 22 | # TODO: should we wildcard other smaller elemBits to VPANDQ or 23 | # VPANDD? Looks like elemBits doesn't really matter afterall in bitwise operations. 24 | - go: MaskedAnd 25 | asm: "VPAND[DQ]" 26 | in: 27 | - class: mask 28 | - *any 29 | - *any 30 | out: 31 | - *any 32 | 33 | - go: AndNot 34 | asm: "VPANDN" 35 | in: 36 | - *any 37 | - *any 38 | out: 39 | - *any 40 | - go: MaskedAndNot 41 | asm: "VPANDN[DQ]" 42 | in: 43 | - class: mask 44 | - *any 45 | - *any 46 | out: 47 | - *any 48 | 49 | - go: Or 50 | asm: "VPOR" 51 | in: 52 | - *any 53 | - *any 54 | out: 55 | - *any 56 | - go: MaskedOr 57 | asm: "VPOR[DQ]" 58 | in: 59 | - class: mask 60 | - *any 61 | - *any 62 | out: 63 | - *any 64 | 65 | - go: Xor 66 | asm: "VPXOR" 67 | in: 68 | - *any 69 | - *any 70 | out: 71 | - *any 72 | - go: MaskedXor 73 | asm: "VPXOR[DQ]" 74 | in: 75 | - class: mask 76 | - *any 77 | - *any 78 | out: 79 | - *any 80 | 81 | # FP operations. 82 | # Set the [base] to be "int" to not include duplicates(excluding "uint"). 83 | # [base] is not used when [overwriteBase] is present. 84 | - go: And 85 | asm: "VANDP[SD]" 86 | in: 87 | - &intToFloat 88 | go: $t 89 | base: int 90 | overwriteBase: float 91 | - *intToFloat 92 | out: 93 | - *intToFloat 94 | - go: MaskedAnd 95 | asm: "VANDP[SD]" 96 | in: 97 | - class: mask 98 | - *intToFloat 99 | - *intToFloat 100 | out: 101 | - *intToFloat 102 | 103 | - go: AndNot 104 | asm: "VANDNP[SD]" 105 | in: 106 | - *intToFloat 107 | - *intToFloat 108 | out: 109 | - *intToFloat 110 | - go: MaskedAndNot 111 | asm: "VANDNP[SD]" 112 | in: 113 | - class: mask 114 | - *intToFloat 115 | - *intToFloat 116 | out: 117 | - *intToFloat 118 | 119 | - go: Or 120 | asm: "VORP[SD]" 121 | in: 122 | - *intToFloat 123 | - *intToFloat 124 | out: 125 | - *intToFloat 126 | - go: MaskedOr 127 | asm: "VORP[SD]" 128 | in: 129 | - class: mask 130 | - *intToFloat 131 | - *intToFloat 132 | out: 133 | - *intToFloat 134 | 135 | - go: Xor 136 | asm: "VXORP[SD]" 137 | in: 138 | - *intToFloat 139 | - *intToFloat 140 | out: 141 | - *intToFloat 142 | - go: MaskedXor 143 | asm: "VXORP[SD]" 144 | in: 145 | - class: mask 146 | - *intToFloat 147 | - *intToFloat 148 | out: 149 | - *intToFloat -------------------------------------------------------------------------------- /internal/simdgen/ops/Compares/categories.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | # const imm predicate(holds for both float and int|uint): 3 | # 0: Equal 4 | # 1: Less 5 | # 2: LessEqual 6 | # 4: NotEqual 7 | # 5: GreaterEqual 8 | # 6: Greater 9 | - go: Equal 10 | constImm: 0 11 | commutative: "true" 12 | extension: "AVX.*" 13 | documentation: "Predicate immediate is 0 if it has;" 14 | - go: Less 15 | constImm: 1 16 | commutative: "false" 17 | extension: "AVX.*" 18 | documentation: "Predicate immediate is 1 if it has;" 19 | - go: LessEqual 20 | constImm: 2 21 | commutative: "false" 22 | extension: "AVX.*" 23 | documentation: "Predicate immediate is 2 if it has;" 24 | - go: IsNan # For float only. 25 | constImm: 3 26 | commutative: "true" 27 | extension: "AVX.*" 28 | documentation: "Predicate immediate is 3 if it has; Returns mask element True if either one of the input\\'s element is Nan; Please use this method as x\\.IsNan\\(x\\) to check x only;" 29 | - go: NotEqual 30 | constImm: 4 31 | commutative: "true" 32 | extension: "AVX.*" 33 | documentation: "Predicate immediate is 4 if it has;" 34 | - go: GreaterEqual 35 | constImm: 5 36 | commutative: "false" 37 | extension: "AVX.*" 38 | documentation: "Predicate immediate is 5 if it has;" 39 | - go: Greater 40 | constImm: 6 41 | commutative: "false" 42 | extension: "AVX.*" 43 | documentation: "Predicate immediate is 6 if it has;" 44 | 45 | - go: MaskedEqual 46 | constImm: 0 47 | masked: "true" 48 | commutative: "true" 49 | extension: "AVX.*" 50 | documentation: "Predicate immediate is 0 if it has;" 51 | - go: MaskedLess 52 | constImm: 1 53 | masked: "true" 54 | commutative: "false" 55 | extension: "AVX.*" 56 | documentation: "Predicate immediate is 1 if it has;" 57 | - go: MaskedLessEqual 58 | constImm: 2 59 | masked: "true" 60 | commutative: "false" 61 | extension: "AVX.*" 62 | documentation: "Predicate immediate is 2 if it has;" 63 | - go: MaskedIsNan # For float only. 64 | constImm: 3 65 | masked: "true" 66 | commutative: "true" 67 | extension: "AVX.*" 68 | documentation: "Predicate immediate is 3 if it has; Returns mask element True if either one of the input\\'s element is Nan; Please use this method as x\\.IsNan\\(x\\) to check x only;" 69 | - go: MaskedNotEqual 70 | constImm: 4 71 | masked: "true" 72 | commutative: "true" 73 | extension: "AVX.*" 74 | documentation: "Predicate immediate is 4 if it has;" 75 | - go: MaskedGreaterEqual 76 | constImm: 5 77 | masked: "true" 78 | commutative: "false" 79 | extension: "AVX.*" 80 | documentation: "Predicate immediate is 5 if it has;" 81 | - go: MaskedGreater 82 | constImm: 6 83 | masked: "true" 84 | commutative: "false" 85 | extension: "AVX.*" 86 | documentation: "Predicate immediate is 6 if it has;" -------------------------------------------------------------------------------- /internal/simdgen/ops/Compares/go.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | # Ints 3 | - go: Equal 4 | asm: "V?PCMPEQ[BWDQ]" 5 | in: &int2 6 | - &int 7 | go: $t 8 | base: int # Looks like PCMP is on signed integers - but for equals does it really matters? 9 | - *int 10 | out: 11 | - &anyvregToMask 12 | go: $t # We still need the output to be the same shape as inputs. 13 | overwriteBase: uint 14 | overwriteClass: mask 15 | - go: Greater 16 | asm: "V?PCMPGT[BWDQ]" 17 | in: *int2 18 | out: 19 | - *anyvregToMask 20 | - go: MaskedEqual 21 | asm: "V?PCMPEQ[BWDQ]" 22 | in: &maskint2 23 | - class: mask 24 | - *int 25 | - *int 26 | out: 27 | - class: mask 28 | - go: MaskedGreater 29 | asm: "V?PCMPGT[BWDQ]" 30 | in: *maskint2 31 | out: 32 | - class: mask 33 | # The const imm predicated compares after AVX512, please see categories.yaml 34 | # for const imm specification. 35 | - go: Masked(Equal|Greater|Less|LessEqual|GreaterEqual|NotEqual) 36 | asm: "VPCMP[BWDQ]" 37 | in: 38 | - class: mask 39 | - &int 40 | go: $t 41 | base: int 42 | - *int 43 | - class: immediate 44 | const: 0 # Just a placeholder, will be overwritten by const imm porting. 45 | out: 46 | - class: mask 47 | - go: Masked(Equal|Greater|Less|LessEqual|GreaterEqual|NotEqual) 48 | asm: "VPCMPU[BWDQ]" 49 | in: 50 | - class: mask 51 | - &uint 52 | go: $t 53 | base: uint 54 | - *uint 55 | - class: immediate 56 | const: 0 57 | out: 58 | - class: mask 59 | 60 | # Floats 61 | - go: Equal|Greater|Less|LessEqual|GreaterEqual|NotEqual|IsNan 62 | asm: "VCMPP[SD]" 63 | in: 64 | - &float 65 | go: $t 66 | base: float 67 | - *float 68 | - class: immediate 69 | const: 0 70 | out: 71 | - go: $t # We still need the output to be the same shape as inputs. 72 | overwriteBase: uint 73 | overwriteClass: mask 74 | - go: Masked(Equal|Greater|Less|LessEqual|GreaterEqual|NotEqual|IsNan) 75 | asm: "VCMPP[SD]" 76 | in: 77 | - class: mask 78 | - *float 79 | - *float 80 | - class: immediate 81 | const: 0 82 | out: 83 | - class: mask -------------------------------------------------------------------------------- /internal/simdgen/ops/FPonlyArith/categories.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | - go: Div 3 | commutative: "false" 4 | extension: "AVX.*" 5 | - go: MaskedDiv 6 | commutative: "false" 7 | masked: "true" 8 | extension: "AVX.*" 9 | - go: Sqrt 10 | commutative: "false" 11 | extension: "AVX.*" 12 | - go: MaskedSqrt 13 | commutative: "false" 14 | masked: "true" 15 | extension: "AVX.*" 16 | - go: ApproximateReciprocal 17 | commutative: "false" 18 | extension: "AVX.*" 19 | - go: MaskedApproximateReciprocal 20 | commutative: "false" 21 | masked: "true" 22 | extension: "AVX.*" 23 | - go: ApproximateReciprocalOfSqrt 24 | commutative: "false" 25 | extension: "AVX.*" 26 | - go: MaskedApproximateReciprocalOfSqrt 27 | commutative: "false" 28 | masked: "true" 29 | extension: "AVX.*" 30 | - go: MaskedMulByPowOf2 # This operation is all after AVX512, the unmasked version will be generated. 31 | commutative: "false" 32 | masked: "true" 33 | extension: "AVX.*" 34 | -------------------------------------------------------------------------------- /internal/simdgen/ops/FPonlyArith/go.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | - go: Div 3 | asm: "V?DIVP[SD]" 4 | in: &2fp 5 | - &fp 6 | go: $t 7 | base: float 8 | - *fp 9 | out: &1fp 10 | - *fp 11 | - go: MaskedDiv 12 | asm: "V?DIVP[SD]" 13 | in: &1mask2fp 14 | - class: mask 15 | - *fp 16 | - *fp 17 | out: *1fp 18 | - go: Sqrt 19 | asm: "V?SQRTP[SD]" 20 | in: *1fp 21 | out: *1fp 22 | - go: MaskedSqrt 23 | asm: "V?SQRTP[SD]" 24 | in: &1mask1fp 25 | - class: mask 26 | - *fp 27 | out: *1fp 28 | - go: MaskedApproximateReciprocal 29 | asm: "VRCP14P[SD]" 30 | in: *1mask1fp 31 | out: *1fp 32 | - go: ApproximateReciprocalOfSqrt 33 | asm: "V?RSQRTPS" 34 | in: *1fp 35 | out: *1fp 36 | - go: MaskedApproximateReciprocalOfSqrt 37 | asm: "VRSQRT14P[SD]" 38 | in: *1mask1fp 39 | out: *1fp 40 | - go: MaskedMulByPowOf2 41 | asm: "VSCALEFP[SD]" 42 | in: *1mask2fp 43 | out: *1fp -------------------------------------------------------------------------------- /internal/simdgen/ops/MinMax/categories.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | - go: Max 3 | commutative: "true" 4 | extension: "AVX.*" 5 | - go: MaskedMax 6 | commutative: "true" 7 | masked: "true" 8 | extension: "AVX.*" 9 | - go: Min 10 | commutative: "true" 11 | extension: "AVX.*" 12 | - go: MaskedMin 13 | commutative: "true" 14 | masked: "true" 15 | extension: "AVX.*" 16 | -------------------------------------------------------------------------------- /internal/simdgen/ops/MinMax/go.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | - go: Max 3 | asm: "V?PMAXS[BWDQ]" 4 | in: &2int 5 | - &int 6 | go: $t 7 | base: int 8 | - *int 9 | out: &1int 10 | - *int 11 | - go: Max 12 | asm: "V?PMAXU[BWDQ]" 13 | in: &2uint 14 | - &uint 15 | go: $t 16 | base: uint 17 | - *uint 18 | out: &1uint 19 | - *uint 20 | - go: MaskedMax 21 | asm: "V?PMAXS[BWDQ]" 22 | in: &1mask2int 23 | - class: mask 24 | - *int 25 | - *int 26 | out: *1int 27 | - go: MaskedMax 28 | asm: "V?PMAXU[BWDQ]" 29 | in: &1mask2uint 30 | - class: mask 31 | - *uint 32 | - *uint 33 | out: *1uint 34 | 35 | - go: Min 36 | asm: "V?PMINS[BWDQ]" 37 | in: *2int 38 | out: *1int 39 | - go: Min 40 | asm: "V?PMINU[BWDQ]" 41 | in: *2uint 42 | out: *1uint 43 | - go: MaskedMin 44 | asm: "V?PMINS[BWDQ]" 45 | in: *1mask2int 46 | out: *1int 47 | - go: MaskedMin 48 | asm: "V?PMINU[BWDQ]" 49 | in: *1mask2uint 50 | out: *1uint 51 | 52 | - go: Max 53 | asm: "V?MAXP[SD]" 54 | in: &2float 55 | - &float 56 | go: $t 57 | base: float 58 | - *float 59 | out: &1float 60 | - *float 61 | - go: MaskedMax 62 | asm: "V?MAXP[SD]" 63 | in: &1mask2float 64 | - class: mask 65 | - *float 66 | - *float 67 | out: *1float 68 | - go: Min 69 | asm: "V?MINP[SD]" 70 | in: *2float 71 | out: *1float 72 | - go: MaskedMin 73 | asm: "V?MINP[SD]" 74 | in: *1mask2float 75 | out: *1float -------------------------------------------------------------------------------- /internal/simdgen/ops/Mul/categories.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | - go: Mul 3 | commutative: "true" 4 | extension: "AVX.*" 5 | - go: MulEvenWiden 6 | commutative: "true" 7 | extension: "AVX.*" 8 | documentation: "Multiplies the even index elements from the two sources of size X at index i, store the result of size 2X at index i/2" 9 | - go: MulHigh 10 | commutative: "true" 11 | extension: "AVX.*" 12 | documentation: "Multiplies the elements from the two sources of size X at index i, store the high X bits of the result of size 2X at index i" 13 | - go: MulLow 14 | commutative: "true" 15 | extension: "AVX.*" 16 | documentation: "Multiplies the elements from the two sources of size X at index i, store the low X bits of the result of size 2X at index i" 17 | - go: MaskedMul 18 | masked: "true" 19 | commutative: "true" 20 | extension: "AVX.*" 21 | - go: MaskedMulEvenWiden 22 | masked: "true" 23 | commutative: "true" 24 | extension: "AVX.*" 25 | documentation: "Multiplies the even index elements from the two sources of size X at index i, store the result of size 2X at index i/2" 26 | - go: MaskedMulHigh 27 | masked: "true" 28 | commutative: "true" 29 | extension: "AVX.*" 30 | documentation: "Multiplies the elements from the two sources of size X at index i, store the high X bits of the result of size 2X at index i" 31 | - go: MaskedMulLow 32 | masked: "true" 33 | commutative: "true" 34 | extension: "AVX.*" 35 | documentation: "Multiplies the elements from the two sources of size X at index i, store the low X bits of the result of size 2X at index i" -------------------------------------------------------------------------------- /internal/simdgen/ops/Mul/go.yaml: -------------------------------------------------------------------------------- 1 | !sum 2 | # "Normal" multiplication is only available for floats. 3 | # This only covers the single and double precision. 4 | - go: Mul 5 | asm: "VMULP[SD]" 6 | in: 7 | - &fp 8 | go: $t 9 | base: float 10 | - *fp 11 | out: 12 | - *fp 13 | - go: MaskedMul 14 | asm: "VMULP[SD]" 15 | in: 16 | - class: mask 17 | - *fp 18 | - *fp 19 | out: 20 | - *fp 21 | 22 | # Integer multiplications. 23 | 24 | # MulEvenWiden 25 | # Dword only. 26 | - go: MulEvenWiden 27 | asm: "VPMULDQ" 28 | in: 29 | - &int 30 | go: $t 31 | base: int 32 | - *int 33 | out: 34 | - &int2 35 | go: $t2 36 | base: int 37 | - go: MulEvenWiden 38 | asm: "VPMULUDQ" 39 | in: 40 | - &uint 41 | go: $t 42 | base: uint 43 | - *uint 44 | out: 45 | - &uint2 46 | go: $t2 47 | base: uint 48 | - go: MaskedMulEvenWiden 49 | asm: "VPMULDQ" 50 | in: 51 | - class: mask 52 | - *int 53 | - *int 54 | out: 55 | - *int2 56 | - go: MaskedMulEvenWiden 57 | asm: "VPMULUDQ" 58 | in: 59 | - class: mask 60 | - *uint 61 | - *uint 62 | out: 63 | - *uint2 64 | 65 | # MulHigh 66 | # Word only. 67 | # Non-masked 68 | - go: MulHigh 69 | asm: "VPMULHW" 70 | in: 71 | - *int 72 | - *int 73 | out: 74 | - *int2 75 | - go: MulHigh 76 | asm: "VPMULHUW" 77 | in: 78 | - *uint 79 | - *uint 80 | out: 81 | - *uint2 82 | - go: MaskedMulHigh 83 | asm: "VPMULHW" 84 | in: 85 | - class: mask 86 | - *int 87 | - *int 88 | out: 89 | - *int2 90 | - go: MaskedMulHigh 91 | asm: "VPMULHUW" 92 | in: 93 | - class: mask 94 | - *uint 95 | - *uint 96 | out: 97 | - *uint2 98 | 99 | # MulLow 100 | # Signed int only. 101 | # Non-masked 102 | - go: MulLow 103 | asm: "VPMULL[WDQ]" 104 | in: 105 | - *int 106 | - *int 107 | out: 108 | - *int2 109 | - go: MaskedMulLow 110 | asm: "VPMULL[WDQ]" 111 | in: 112 | - class: mask 113 | - *int 114 | - *int 115 | out: 116 | - *int2 -------------------------------------------------------------------------------- /internal/simdgen/ops/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | const baseDir = "ops" // The main directory containing A, B, C, etc. 11 | 12 | func main() { 13 | if err := mergeYamlFiles("categories.yaml"); err != nil { 14 | fmt.Printf("Error processing categories.yaml: %v\n", err) 15 | os.Exit(1) 16 | } 17 | if err := mergeYamlFiles("go.yaml"); err != nil { 18 | fmt.Printf("Error processing go.yaml: %v\n", err) 19 | os.Exit(1) 20 | } 21 | } 22 | 23 | func mergeYamlFiles(targetFileName string) error { 24 | outputFile, err := os.Create(targetFileName) 25 | if err != nil { 26 | return fmt.Errorf("failed to create output file %s: %w", targetFileName, err) 27 | } 28 | defer outputFile.Close() 29 | 30 | writer := bufio.NewWriter(outputFile) 31 | _, err = writer.WriteString("!sum\n") 32 | if err != nil { 33 | return fmt.Errorf("failed to write '!sum' to %s: %w", targetFileName, err) 34 | } 35 | 36 | entries, err := os.ReadDir(baseDir) 37 | if err != nil { 38 | return fmt.Errorf("failed to read base directory %s: %w", baseDir, err) 39 | } 40 | for _, entry := range entries { 41 | if !entry.IsDir() { 42 | continue 43 | } 44 | 45 | subdirPath := filepath.Join(baseDir, entry.Name()) 46 | sourceFilePath := filepath.Join(subdirPath, targetFileName) 47 | 48 | sourceFile, err := os.Open(sourceFilePath) 49 | if err != nil { 50 | if os.IsNotExist(err) { 51 | fmt.Printf("Skipping: %s not found in %s\n", targetFileName, subdirPath) 52 | continue 53 | } 54 | return fmt.Errorf("failed to open source file %s: %w", sourceFilePath, err) 55 | } 56 | defer sourceFile.Close() 57 | 58 | scanner := bufio.NewScanner(sourceFile) 59 | // Skip first line 60 | scanner.Scan() 61 | // Append the rest of the lines to the output file 62 | for scanner.Scan() { 63 | line := scanner.Text() 64 | _, err = writer.WriteString(line + "\n") 65 | if err != nil { 66 | return fmt.Errorf("failed to write line from %s to %s: %w", sourceFilePath, targetFileName, err) 67 | } 68 | } 69 | 70 | if err := scanner.Err(); err != nil { 71 | return fmt.Errorf("error reading lines from %s: %w", sourceFilePath, err) 72 | } 73 | } 74 | return writer.Flush() 75 | } 76 | -------------------------------------------------------------------------------- /internal/simdgen/types.yaml: -------------------------------------------------------------------------------- 1 | # This file defines the possible types of each operand and result. 2 | # 3 | # In general, we're able to narrow this down on some attributes directly from 4 | # the machine instruction descriptions, but the Go mappings need to further 5 | # constrain them and how they relate. For example, on x86 we can't distinguish 6 | # int and uint, though we can distinguish these from float. 7 | 8 | in: !repeat 9 | - !sum &types 10 | - {class: vreg, go: Int8x16, base: "int", elemBits: 8, bits: 128, lanes: 16} 11 | - {class: vreg, go: Uint8x16, base: "uint", elemBits: 8, bits: 128, lanes: 16} 12 | - {class: vreg, go: Int16x8, base: "int", elemBits: 16, bits: 128, lanes: 8} 13 | - {class: vreg, go: Uint16x8, base: "uint", elemBits: 16, bits: 128, lanes: 8} 14 | - {class: vreg, go: Int32x4, base: "int", elemBits: 32, bits: 128, lanes: 4} 15 | - {class: vreg, go: Uint32x4, base: "uint", elemBits: 32, bits: 128, lanes: 4} 16 | - {class: vreg, go: Int64x2, base: "int", elemBits: 64, bits: 128, lanes: 2} 17 | - {class: vreg, go: Uint64x2, base: "uint", elemBits: 64, bits: 128, lanes: 2} 18 | - {class: vreg, go: Float32x4, base: "float", elemBits: 32, bits: 128, lanes: 4} 19 | - {class: vreg, go: Float64x2, base: "float", elemBits: 64, bits: 128, lanes: 2} 20 | - {class: vreg, go: Int8x32, base: "int", elemBits: 8, bits: 256, lanes: 32} 21 | - {class: vreg, go: Uint8x32, base: "uint", elemBits: 8, bits: 256, lanes: 32} 22 | - {class: vreg, go: Int16x16, base: "int", elemBits: 16, bits: 256, lanes: 16} 23 | - {class: vreg, go: Uint16x16, base: "uint", elemBits: 16, bits: 256, lanes: 16} 24 | - {class: vreg, go: Int32x8, base: "int", elemBits: 32, bits: 256, lanes: 8} 25 | - {class: vreg, go: Uint32x8, base: "uint", elemBits: 32, bits: 256, lanes: 8} 26 | - {class: vreg, go: Int64x4, base: "int", elemBits: 64, bits: 256, lanes: 4} 27 | - {class: vreg, go: Uint64x4, base: "uint", elemBits: 64, bits: 256, lanes: 4} 28 | - {class: vreg, go: Float32x8, base: "float", elemBits: 32, bits: 256, lanes: 8} 29 | - {class: vreg, go: Float64x4, base: "float", elemBits: 64, bits: 256, lanes: 4} 30 | - {class: vreg, go: Int8x64, base: "int", elemBits: 8, bits: 512, lanes: 64} 31 | - {class: vreg, go: Uint8x64, base: "uint", elemBits: 8, bits: 512, lanes: 64} 32 | - {class: vreg, go: Int16x32, base: "int", elemBits: 16, bits: 512, lanes: 32} 33 | - {class: vreg, go: Uint16x32, base: "uint", elemBits: 16, bits: 512, lanes: 32} 34 | - {class: vreg, go: Int32x16, base: "int", elemBits: 32, bits: 512, lanes: 16} 35 | - {class: vreg, go: Uint32x16, base: "uint", elemBits: 32, bits: 512, lanes: 16} 36 | - {class: vreg, go: Int64x8, base: "int", elemBits: 64, bits: 512, lanes: 8} 37 | - {class: vreg, go: Uint64x8, base: "uint", elemBits: 64, bits: 512, lanes: 8} 38 | - {class: vreg, go: Float32x16, base: "float", elemBits: 32, bits: 512, lanes: 16} 39 | - {class: vreg, go: Float64x8, base: "float", elemBits: 64, bits: 512, lanes: 8} 40 | - {class: mask, go: Mask8x16, base: "int", elemBits: 8, bits: 128, lanes: 16} 41 | - {class: mask, go: Mask16x8, base: "int", elemBits: 16, bits: 128, lanes: 8} 42 | - {class: mask, go: Mask32x4, base: "int", elemBits: 32, bits: 128, lanes: 4} 43 | - {class: mask, go: Mask64x2, base: "int", elemBits: 64, bits: 128, lanes: 2} 44 | - {class: mask, go: Mask8x32, base: "int", elemBits: 8, bits: 256, lanes: 32} 45 | - {class: mask, go: Mask16x16, base: "int", elemBits: 16, bits: 256, lanes: 16} 46 | - {class: mask, go: Mask32x8, base: "int", elemBits: 32, bits: 256, lanes: 8} 47 | - {class: mask, go: Mask64x4, base: "int", elemBits: 64, bits: 256, lanes: 4} 48 | - {class: mask, go: Mask8x64, base: "int", elemBits: 8, bits: 512, lanes: 64} 49 | - {class: mask, go: Mask16x32, base: "int", elemBits: 16, bits: 512, lanes: 32} 50 | - {class: mask, go: Mask32x16, base: "int", elemBits: 32, bits: 512, lanes: 16} 51 | - {class: mask, go: Mask64x8, base: "int", elemBits: 64, bits: 512, lanes: 8} 52 | - {class: immediate, go: Immediate} # TODO: we only support imms that are not used as value -- usually as instruction semantic predicate like VPCMP as of now. 53 | out: !repeat 54 | - *types 55 | -------------------------------------------------------------------------------- /internal/unify/closure.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import ( 8 | "fmt" 9 | "iter" 10 | "maps" 11 | "slices" 12 | ) 13 | 14 | type Closure struct { 15 | val *Value 16 | env nonDetEnv 17 | } 18 | 19 | func NewSum(vs ...*Value) Closure { 20 | id := &ident{name: "sum"} 21 | return Closure{NewValue(Var{id}), topEnv.bind(id, vs...)} 22 | } 23 | 24 | // IsBottom returns whether c consists of no values. 25 | func (c Closure) IsBottom() bool { 26 | return c.val.Domain == nil 27 | } 28 | 29 | // Summands returns the top-level Values of c. This assumes the top-level of c 30 | // was constructed as a sum, and is mostly useful for debugging. 31 | func (c Closure) Summands() iter.Seq[*Value] { 32 | if v, ok := c.val.Domain.(Var); ok { 33 | parts := c.env.partitionBy(v.id) 34 | return func(yield func(*Value) bool) { 35 | for _, part := range parts { 36 | if !yield(part.value) { 37 | return 38 | } 39 | } 40 | } 41 | } 42 | return func(yield func(*Value) bool) { 43 | yield(c.val) 44 | } 45 | } 46 | 47 | // All enumerates all possible concrete values of c by substituting variables 48 | // from the environment. 49 | // 50 | // E.g., enumerating this Value 51 | // 52 | // a: !sum [1, 2] 53 | // b: !sum [3, 4] 54 | // 55 | // results in 56 | // 57 | // - {a: 1, b: 3} 58 | // - {a: 1, b: 4} 59 | // - {a: 2, b: 3} 60 | // - {a: 2, b: 4} 61 | func (c Closure) All() iter.Seq[*Value] { 62 | // In order to enumerate all concrete values under all possible variable 63 | // bindings, we use a "non-deterministic continuation passing style" to 64 | // implement this. We use CPS to traverse the Value tree, threading the 65 | // (possibly narrowing) environment through that CPS following an Euler 66 | // tour. Where the environment permits multiple choices, we invoke the same 67 | // continuation for each choice. Similar to a yield function, the 68 | // continuation can return false to stop the non-deterministic walk. 69 | return func(yield func(*Value) bool) { 70 | c.val.all1(c.env, func(v *Value, e nonDetEnv) bool { 71 | return yield(v) 72 | }) 73 | } 74 | } 75 | 76 | func (v *Value) all1(e nonDetEnv, cont func(*Value, nonDetEnv) bool) bool { 77 | switch d := v.Domain.(type) { 78 | default: 79 | panic(fmt.Sprintf("unknown domain type %T", d)) 80 | 81 | case nil: 82 | return true 83 | 84 | case Top, String: 85 | return cont(v, e) 86 | 87 | case Def: 88 | fields := d.keys() 89 | // We can reuse this parts slice because we're doing a DFS through the 90 | // state space. (Otherwise, we'd have to do some messy threading of an 91 | // immutable slice-like value through allElt.) 92 | parts := make(map[string]*Value, len(fields)) 93 | 94 | // TODO: If there are no Vars or Sums under this Def, then nothing can 95 | // change the Value or env, so we could just cont(v, e). 96 | var allElt func(elt int, e nonDetEnv) bool 97 | allElt = func(elt int, e nonDetEnv) bool { 98 | if elt == len(fields) { 99 | // Build a new Def from the concrete parts. Clone parts because 100 | // we may reuse it on other non-deterministic branches. 101 | nVal := newValueFrom(Def{maps.Clone(parts)}, v) 102 | return cont(nVal, e) 103 | } 104 | 105 | return d.fields[fields[elt]].all1(e, func(v *Value, e nonDetEnv) bool { 106 | parts[fields[elt]] = v 107 | return allElt(elt+1, e) 108 | }) 109 | } 110 | return allElt(0, e) 111 | 112 | case Tuple: 113 | // Essentially the same as Def. 114 | if d.repeat != nil { 115 | // There's nothing we can do with this. 116 | return cont(v, e) 117 | } 118 | parts := make([]*Value, len(d.vs)) 119 | var allElt func(elt int, e nonDetEnv) bool 120 | allElt = func(elt int, e nonDetEnv) bool { 121 | if elt == len(d.vs) { 122 | // Build a new tuple from the concrete parts. Clone parts because 123 | // we may reuse it on other non-deterministic branches. 124 | nVal := newValueFrom(Tuple{vs: slices.Clone(parts)}, v) 125 | return cont(nVal, e) 126 | } 127 | 128 | return d.vs[elt].all1(e, func(v *Value, e nonDetEnv) bool { 129 | parts[elt] = v 130 | return allElt(elt+1, e) 131 | }) 132 | } 133 | return allElt(0, e) 134 | 135 | case Var: 136 | // Go each way this variable can be bound. 137 | for _, ePart := range e.partitionBy(d.id) { 138 | // d.id is no longer bound in this environment partition. We'll may 139 | // need it later in the Euler tour, so bind it back to this single 140 | // value. 141 | env := ePart.env.bind(d.id, ePart.value) 142 | if !ePart.value.all1(env, cont) { 143 | return false 144 | } 145 | } 146 | return true 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /internal/unify/dot.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "html" 11 | "io" 12 | "os" 13 | "os/exec" 14 | "strings" 15 | ) 16 | 17 | const maxNodes = 30 18 | 19 | type dotEncoder struct { 20 | w *bytes.Buffer 21 | 22 | idGen int // Node name generation 23 | valLimit int // Limit the number of Values in a subgraph 24 | 25 | idp identPrinter 26 | } 27 | 28 | func newDotEncoder() *dotEncoder { 29 | return &dotEncoder{ 30 | w: new(bytes.Buffer), 31 | } 32 | } 33 | 34 | func (enc *dotEncoder) clear() { 35 | enc.w.Reset() 36 | enc.idGen = 0 37 | } 38 | 39 | func (enc *dotEncoder) writeTo(w io.Writer) { 40 | fmt.Fprintln(w, "digraph {") 41 | // Use the "new" ranking algorithm, which lets us put nodes from different 42 | // clusters in the same rank. 43 | fmt.Fprintln(w, "newrank=true;") 44 | fmt.Fprintln(w, "node [shape=box, ordering=out];") 45 | 46 | w.Write(enc.w.Bytes()) 47 | fmt.Fprintln(w, "}") 48 | } 49 | 50 | func (enc *dotEncoder) writeSvg(w io.Writer) error { 51 | cmd := exec.Command("dot", "-Tsvg") 52 | in, err := cmd.StdinPipe() 53 | if err != nil { 54 | return err 55 | } 56 | var out bytes.Buffer 57 | cmd.Stdout = &out 58 | cmd.Stderr = os.Stderr 59 | if err := cmd.Start(); err != nil { 60 | return err 61 | } 62 | enc.writeTo(in) 63 | in.Close() 64 | if err := cmd.Wait(); err != nil { 65 | return err 66 | } 67 | // Trim SVG header so the result can be embedded 68 | // 69 | // TODO: In Graphviz 10.0.1, we could use -Tsvg_inline. 70 | svg := out.Bytes() 71 | if i := bytes.Index(svg, []byte("= 0 { 72 | svg = svg[i:] 73 | } 74 | _, err = w.Write(svg) 75 | return err 76 | } 77 | 78 | func (enc *dotEncoder) newID(f string) string { 79 | id := fmt.Sprintf(f, enc.idGen) 80 | enc.idGen++ 81 | return id 82 | } 83 | 84 | func (enc *dotEncoder) node(label, sublabel string) string { 85 | id := enc.newID("n%d") 86 | l := html.EscapeString(label) 87 | if sublabel != "" { 88 | l += fmt.Sprintf("
%s", html.EscapeString(sublabel)) 89 | } 90 | fmt.Fprintf(enc.w, "%s [label=<%s>];\n", id, l) 91 | return id 92 | } 93 | 94 | func (enc *dotEncoder) edge(from, to string, label string, args ...any) { 95 | l := fmt.Sprintf(label, args...) 96 | fmt.Fprintf(enc.w, "%s -> %s [label=%q];\n", from, to, l) 97 | } 98 | 99 | func (enc *dotEncoder) subgraph(v *Value) (vID, cID string) { 100 | enc.valLimit = maxNodes 101 | cID = enc.newID("cluster_%d") 102 | fmt.Fprintf(enc.w, "subgraph %s {\n", cID) 103 | fmt.Fprintf(enc.w, "style=invis;") 104 | vID = enc.value(v) 105 | fmt.Fprintf(enc.w, "}\n") 106 | return 107 | } 108 | 109 | func (enc *dotEncoder) value(v *Value) string { 110 | if enc.valLimit <= 0 { 111 | id := enc.newID("n%d") 112 | fmt.Fprintf(enc.w, "%s [label=\"...\", shape=triangle];\n", id) 113 | return id 114 | } 115 | enc.valLimit-- 116 | 117 | switch vd := v.Domain.(type) { 118 | default: 119 | panic(fmt.Sprintf("unknown domain type %T", vd)) 120 | 121 | case nil: 122 | return enc.node("_|_", "") 123 | 124 | case Top: 125 | return enc.node("_", "") 126 | 127 | // TODO: Like in YAML, figure out if this is just a sum. In dot, we 128 | // could say any unentangled variable is a sum, and if it has more than 129 | // one reference just share the node. 130 | 131 | // case Sum: 132 | // node := enc.node("Sum", "") 133 | // for i, elt := range vd.vs { 134 | // enc.edge(node, enc.value(elt), "%d", i) 135 | // if enc.valLimit <= 0 { 136 | // break 137 | // } 138 | // } 139 | // return node 140 | 141 | case Def: 142 | node := enc.node("Def", "") 143 | for k, v := range vd.All() { 144 | enc.edge(node, enc.value(v), "%s", k) 145 | if enc.valLimit <= 0 { 146 | break 147 | } 148 | } 149 | return node 150 | 151 | case Tuple: 152 | if vd.repeat == nil { 153 | label := "Tuple" 154 | node := enc.node(label, "") 155 | for i, elt := range vd.vs { 156 | enc.edge(node, enc.value(elt), "%d", i) 157 | if enc.valLimit <= 0 { 158 | break 159 | } 160 | } 161 | return node 162 | } else { 163 | // TODO 164 | return enc.node("TODO: Repeat", "") 165 | } 166 | 167 | case String: 168 | switch vd.kind { 169 | case stringExact: 170 | return enc.node(fmt.Sprintf("%q", vd.exact), "") 171 | case stringRegex: 172 | var parts []string 173 | for _, re := range vd.re { 174 | parts = append(parts, fmt.Sprintf("%q", re)) 175 | } 176 | return enc.node(strings.Join(parts, "&"), "") 177 | } 178 | panic("bad String kind") 179 | 180 | case Var: 181 | return enc.node(fmt.Sprintf("Var %s", enc.idp.unique(vd.id)), "") 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /internal/unify/html.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import ( 8 | "fmt" 9 | "html" 10 | "io" 11 | "strings" 12 | ) 13 | 14 | func (t *tracer) writeHTML(w io.Writer) { 15 | if !t.saveTree { 16 | panic("writeHTML called without tracer.saveTree") 17 | } 18 | 19 | fmt.Fprintf(w, "", htmlCSS) 20 | for _, root := range t.trees { 21 | dot := newDotEncoder() 22 | html := htmlTracer{w: w, dot: dot} 23 | html.writeTree(root) 24 | } 25 | fmt.Fprintf(w, "\n") 26 | } 27 | 28 | const htmlCSS = ` 29 | .unify { 30 | display: grid; 31 | grid-auto-columns: min-content; 32 | text-align: center; 33 | } 34 | 35 | .header { 36 | grid-row: 1; 37 | font-weight: bold; 38 | padding: 0.25em; 39 | position: sticky; 40 | top: 0; 41 | background: white; 42 | } 43 | 44 | .envFactor { 45 | display: grid; 46 | grid-auto-rows: min-content; 47 | grid-template-columns: subgrid; 48 | text-align: center; 49 | } 50 | ` 51 | 52 | type htmlTracer struct { 53 | w io.Writer 54 | dot *dotEncoder 55 | svgs map[*Value]string 56 | } 57 | 58 | func (t *htmlTracer) writeTree(node *traceTree) { 59 | // TODO: This could be really nice. 60 | // 61 | // - Put nodes that were unified on the same rank with {rank=same; a; b} 62 | // 63 | // - On hover, highlight nodes that node was unified with and the result. If 64 | // it's a variable, highlight it in the environment, too. 65 | // 66 | // - On click, show the details of unifying that node. 67 | // 68 | // This could be the only way to navigate, without necessarily needing the 69 | // whole nest of nodes. 70 | 71 | // TODO: It might be possible to write this out on the fly. 72 | 73 | t.emit([]*Value{node.v, node.w}, []string{"v", "w"}, node.envIn) 74 | 75 | // Render children. 76 | for i, child := range node.children { 77 | if i >= 10 { 78 | fmt.Fprintf(t.w, `
...
`) 79 | break 80 | } 81 | fmt.Fprintf(t.w, `
%s`, html.EscapeString(child.label)) 82 | t.writeTree(child) 83 | fmt.Fprintf(t.w, "
\n") 84 | } 85 | 86 | // Render result. 87 | if node.err != nil { 88 | fmt.Fprintf(t.w, "Error: %s\n", html.EscapeString(node.err.Error())) 89 | } else { 90 | t.emit([]*Value{node.res}, []string{"res"}, node.env) 91 | } 92 | } 93 | 94 | func (t *htmlTracer) svg(v *Value) string { 95 | if s, ok := t.svgs[v]; ok { 96 | return s 97 | } 98 | var buf strings.Builder 99 | t.dot.subgraph(v) 100 | t.dot.writeSvg(&buf) 101 | t.dot.clear() 102 | svg := buf.String() 103 | if t.svgs == nil { 104 | t.svgs = make(map[*Value]string) 105 | } 106 | t.svgs[v] = svg 107 | buf.Reset() 108 | return svg 109 | } 110 | 111 | func (t *htmlTracer) emit(vs []*Value, labels []string, env nonDetEnv) { 112 | fmt.Fprintf(t.w, `
`) 113 | for i, v := range vs { 114 | fmt.Fprintf(t.w, `
%s
`, i+1, html.EscapeString(labels[i])) 115 | fmt.Fprintf(t.w, `
%s
`, i+1, t.svg(v)) 116 | } 117 | 118 | t.emitEnv(env, len(vs)) 119 | 120 | fmt.Fprintf(t.w, `
`) 121 | } 122 | 123 | func (t *htmlTracer) emitEnv(env nonDetEnv, colStart int) { 124 | if env.isBottom() { 125 | fmt.Fprintf(t.w, `
_|_
`, colStart+1) 126 | return 127 | } 128 | 129 | colLimit := 10 130 | col := colStart 131 | for i, f := range env.factors { 132 | if i > 0 { 133 | // Print * between each factor. 134 | fmt.Fprintf(t.w, `
×
`, col+1) 135 | col++ 136 | } 137 | 138 | var idCols []int 139 | for i, id := range f.ids { 140 | var str string 141 | if i == 0 && len(f.ids) > 1 { 142 | str = "(" 143 | } 144 | if colLimit <= 0 { 145 | str += "..." 146 | } else { 147 | str += html.EscapeString(t.dot.idp.unique(id)) 148 | } 149 | if (i == len(f.ids)-1 || colLimit <= 0) && len(f.ids) > 1 { 150 | str += ")" 151 | } 152 | 153 | fmt.Fprintf(t.w, `
%s
`, col+1, str) 154 | idCols = append(idCols, col) 155 | 156 | col++ 157 | if colLimit <= 0 { 158 | break 159 | } 160 | colLimit-- 161 | } 162 | 163 | fmt.Fprintf(t.w, `
`, idCols[0]+1, col+1) 164 | rowLimit := 10 165 | row := 0 166 | for _, term := range f.terms { 167 | // TODO: Print + between rows? With some horizontal something to 168 | // make it clear what it applies across? 169 | 170 | for i, val := range term.vals { 171 | fmt.Fprintf(t.w, `
`, row+1, idCols[i]-idCols[0]+1) 172 | if i < len(term.vals)-1 && i == len(idCols)-1 { 173 | fmt.Fprintf(t.w, `...
`) 174 | break 175 | } else if rowLimit <= 0 { 176 | fmt.Fprintf(t.w, `...
`) 177 | } else { 178 | fmt.Fprintf(t.w, `%s`, t.svg(val)) 179 | } 180 | } 181 | 182 | row++ 183 | if rowLimit <= 0 { 184 | break 185 | } 186 | rowLimit-- 187 | } 188 | fmt.Fprintf(t.w, ``) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /internal/unify/pos.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | type Pos struct { 12 | Path string 13 | Line int 14 | } 15 | 16 | func (p Pos) String() string { 17 | var b []byte 18 | b, _ = p.AppendText(b) 19 | return string(b) 20 | } 21 | 22 | func (p Pos) AppendText(b []byte) ([]byte, error) { 23 | if p.Line == 0 { 24 | if p.Path == "" { 25 | return append(b, "?:?"...), nil 26 | } else { 27 | return append(b, p.Path...), nil 28 | } 29 | } else if p.Path == "" { 30 | return fmt.Appendf(b, "?:%d", p.Line), nil 31 | } 32 | return fmt.Appendf(b, "%s:%d", p.Path, p.Line), nil 33 | } 34 | -------------------------------------------------------------------------------- /internal/unify/testdata/unify.yaml: -------------------------------------------------------------------------------- 1 | # Basic tests of unification 2 | 3 | # 4 | # Terminals 5 | # 6 | 7 | unify: 8 | - _ 9 | - _ 10 | want: 11 | _ 12 | --- 13 | unify: 14 | - _ 15 | - test 16 | want: 17 | test 18 | --- 19 | unify: 20 | - test 21 | - t?est 22 | want: 23 | test 24 | --- 25 | unify: 26 | - 1 27 | - 1 28 | want: 29 | 1 30 | --- 31 | unify: 32 | - test 33 | - foo 34 | want: 35 | _|_ 36 | 37 | # 38 | # Tuple 39 | # 40 | 41 | --- 42 | unify: 43 | - [a, b] 44 | - [a, b] 45 | want: 46 | [a, b] 47 | --- 48 | unify: 49 | - [a, _] 50 | - [_, b] 51 | want: 52 | [a, b] 53 | --- 54 | unify: 55 | - ["ab?c", "de?f"] 56 | - [ac, def] 57 | want: 58 | [ac, def] 59 | 60 | # 61 | # Repeats 62 | # 63 | 64 | --- 65 | unify: 66 | - !repeat [a] 67 | - [_] 68 | want: 69 | [a] 70 | --- 71 | unify: 72 | - !repeat [a] 73 | - [_, _] 74 | want: 75 | [a, a] 76 | --- 77 | unify: 78 | - !repeat [a] 79 | - [b] 80 | want: 81 | _|_ 82 | --- 83 | unify: 84 | - !repeat [xy*] 85 | - [x, xy, xyy] 86 | want: 87 | [x, xy, xyy] 88 | --- 89 | unify: 90 | - !repeat [xy*] 91 | - !repeat ["xz?y*"] 92 | - [x, xy, xyy] 93 | want: 94 | [x, xy, xyy] 95 | --- 96 | unify: 97 | - !repeat [!sum [a, b]] 98 | - [a, b, a] 99 | all: 100 | - [a, b, a] 101 | --- 102 | unify: 103 | - !repeat [!sum [a, b]] 104 | - !repeat [!sum [b, c]] 105 | - [b, b, b] 106 | all: 107 | - [b, b, b] 108 | --- 109 | unify: 110 | - !repeat [!sum [a, b]] 111 | - !repeat [!sum [b, c]] 112 | - [a] 113 | all: [] 114 | 115 | # 116 | # Def 117 | # 118 | 119 | --- 120 | unify: 121 | - {a: a, b: b} 122 | - {a: a, b: b} 123 | want: 124 | {a: a, b: b} 125 | --- 126 | unify: 127 | - {a: a} 128 | - {b: b} 129 | want: 130 | {a: a, b: b} 131 | 132 | # 133 | # Sum 134 | # 135 | 136 | --- 137 | unify: 138 | - !sum [1, 2] 139 | - !sum [2, 3] 140 | all: 141 | - 2 142 | --- 143 | unify: 144 | - !sum [{label: a, value: abc}, {label: b, value: def}] 145 | - !sum [{value: "ab?c", extra: d}, {value: "def?", extra: g}] 146 | all: 147 | - {extra: d, label: a, value: abc} 148 | - {extra: g, label: b, value: def} 149 | --- 150 | # A sum of repeats must deal with different dynamically-created variables in 151 | # each branch. 152 | unify: 153 | - !sum [!repeat [a], !repeat [b]] 154 | - [a, a, a] 155 | all: 156 | - [a, a, a] 157 | --- 158 | unify: 159 | - !sum [!repeat [a], !repeat [b]] 160 | - [a, a, b] 161 | all: [] 162 | --- 163 | # Exercise sumEnvs with more than one result 164 | unify: 165 | - !sum 166 | - [a|b, c|d] 167 | - [e, g] 168 | - [!sum [a, b, e, f], !sum [c, d, g, h]] 169 | all: 170 | - [a, c] 171 | - [a, d] 172 | - [b, c] 173 | - [b, d] 174 | - [e, g] 175 | -------------------------------------------------------------------------------- /internal/unify/testdata/vars.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Basic tests 3 | # 4 | 5 | name: "basic string" 6 | unify: 7 | - $x 8 | - test 9 | all: 10 | - test 11 | --- 12 | name: "basic tuple" 13 | unify: 14 | - [$x, $x] 15 | - [test, test] 16 | all: 17 | - [test, test] 18 | --- 19 | name: "three tuples" 20 | unify: 21 | - [$x, $x] 22 | - [test, _] 23 | - [_, test] 24 | all: 25 | - [test, test] 26 | --- 27 | name: "basic def" 28 | unify: 29 | - {a: $x, b: $x} 30 | - {a: test, b: test} 31 | all: 32 | - {a: test, b: test} 33 | --- 34 | name: "three defs" 35 | unify: 36 | - {a: $x, b: $x} 37 | - {a: test} 38 | - {b: test} 39 | all: 40 | - {a: test, b: test} 41 | 42 | # 43 | # Bottom tests 44 | # 45 | 46 | --- 47 | name: "basic bottom" 48 | unify: 49 | - [$x, $x] 50 | - [test, foo] 51 | all: [] 52 | --- 53 | name: "three-way bottom" 54 | unify: 55 | - [$x, $x] 56 | - [test, _] 57 | - [_, foo] 58 | all: [] 59 | 60 | # 61 | # Basic sum tests 62 | # 63 | 64 | --- 65 | name: "basic sum" 66 | unify: 67 | - $x 68 | - !sum [a, b] 69 | all: 70 | - a 71 | - b 72 | --- 73 | name: "sum of tuples" 74 | unify: 75 | - [$x] 76 | - !sum [[a], [b]] 77 | all: 78 | - [a] 79 | - [b] 80 | --- 81 | name: "acausal sum" 82 | unify: 83 | - [_, !sum [a, b]] 84 | - [$x, $x] 85 | all: 86 | - [a, a] 87 | - [b, b] 88 | 89 | # 90 | # Transitivity tests 91 | # 92 | 93 | --- 94 | name: "transitivity" 95 | unify: 96 | - [_, _, _, test] 97 | - [$x, $x, _, _] 98 | - [ _, $x, $x, _] 99 | - [ _, _, $x, $x] 100 | all: 101 | - [test, test, test, test] 102 | 103 | # 104 | # Multiple vars 105 | # 106 | 107 | --- 108 | name: "basic uncorrelated vars" 109 | unify: 110 | - - !sum [1, 2] 111 | - !sum [3, 4] 112 | - - $a 113 | - $b 114 | all: 115 | - [1, 3] 116 | - [1, 4] 117 | - [2, 3] 118 | - [2, 4] 119 | --- 120 | name: "uncorrelated vars" 121 | unify: 122 | - - !sum [1, 2] 123 | - !sum [3, 4] 124 | - !sum [1, 2] 125 | - - $a 126 | - $b 127 | - $a 128 | all: 129 | - [1, 3, 1] 130 | - [1, 4, 1] 131 | - [2, 3, 2] 132 | - [2, 4, 2] 133 | --- 134 | name: "entangled vars" 135 | unify: 136 | - - !sum [[1,2],[3,4]] 137 | - !sum [[2,1],[3,4],[4,3]] 138 | - - [$a, $b] 139 | - [$b, $a] 140 | all: 141 | - - [1, 2] 142 | - [2, 1] 143 | - - [3, 4] 144 | - [4, 3] 145 | 146 | # 147 | # End-to-end examples 148 | # 149 | 150 | --- 151 | name: "end-to-end" 152 | unify: 153 | - go: Add 154 | in: 155 | - go: $t 156 | - go: $t 157 | - in: !repeat 158 | - !sum 159 | - go: Int32x4 160 | base: int 161 | - go: Uint32x4 162 | base: uint 163 | all: 164 | - go: Add 165 | in: 166 | - base: int 167 | go: Int32x4 168 | - base: int 169 | go: Int32x4 170 | - go: Add 171 | in: 172 | - base: uint 173 | go: Uint32x4 174 | - base: uint 175 | go: Uint32x4 176 | -------------------------------------------------------------------------------- /internal/unify/trace.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "strings" 11 | 12 | "gopkg.in/yaml.v3" 13 | ) 14 | 15 | // debugDotInHTML, if true, includes dot code for all graphs in the HTML. Useful 16 | // for debugging the dot output itself. 17 | const debugDotInHTML = false 18 | 19 | var Debug struct { 20 | // UnifyLog, if non-nil, receives a streaming text trace of unification. 21 | UnifyLog io.Writer 22 | 23 | // HTML, if non-nil, writes an HTML trace of unification to HTML. 24 | HTML io.Writer 25 | } 26 | 27 | type tracer struct { 28 | logw io.Writer 29 | 30 | enc yamlEncoder // Print consistent idents throughout 31 | 32 | saveTree bool // if set, record tree; required for HTML output 33 | 34 | path []string 35 | 36 | node *traceTree 37 | trees []*traceTree 38 | } 39 | 40 | type traceTree struct { 41 | label string // Identifies this node as a child of parent 42 | v, w *Value // Unification inputs 43 | envIn nonDetEnv 44 | res *Value // Unification result 45 | env nonDetEnv 46 | err error // or error 47 | 48 | parent *traceTree 49 | children []*traceTree 50 | } 51 | 52 | type tracerExit struct { 53 | t *tracer 54 | len int 55 | node *traceTree 56 | } 57 | 58 | func (t *tracer) enter(pat string, vals ...any) tracerExit { 59 | if t == nil { 60 | return tracerExit{} 61 | } 62 | 63 | label := fmt.Sprintf(pat, vals...) 64 | 65 | var p *traceTree 66 | if t.saveTree { 67 | p = t.node 68 | if p != nil { 69 | t.node = &traceTree{label: label, parent: p} 70 | p.children = append(p.children, t.node) 71 | } 72 | } 73 | 74 | t.path = append(t.path, label) 75 | return tracerExit{t, len(t.path) - 1, p} 76 | } 77 | 78 | func (t *tracer) enterVar(id *ident, branch int) tracerExit { 79 | if t == nil { 80 | return tracerExit{} 81 | } 82 | 83 | // Use the tracer's ident printer 84 | return t.enter("Var %s br %d", t.enc.idp.unique(id), branch) 85 | } 86 | 87 | func (te tracerExit) exit() { 88 | if te.t == nil { 89 | return 90 | } 91 | te.t.path = te.t.path[:te.len] 92 | te.t.node = te.node 93 | } 94 | 95 | func indentf(prefix string, pat string, vals ...any) string { 96 | s := fmt.Sprintf(pat, vals...) 97 | if len(prefix) == 0 { 98 | return s 99 | } 100 | if !strings.Contains(s, "\n") { 101 | return prefix + s 102 | } 103 | 104 | indent := prefix 105 | if strings.TrimLeft(prefix, " ") != "" { 106 | // Prefix has non-space characters in it. Construct an all space-indent. 107 | indent = strings.Repeat(" ", len(prefix)) 108 | } 109 | return prefix + strings.ReplaceAll(s, "\n", "\n"+indent) 110 | } 111 | 112 | func yamlf(prefix string, node *yaml.Node) string { 113 | b, err := yaml.Marshal(node) 114 | if err != nil { 115 | return fmt.Sprintf("", err) 116 | } 117 | return strings.TrimRight(indentf(prefix, "%s", b), " \n") 118 | } 119 | 120 | func (t *tracer) logf(pat string, vals ...any) { 121 | if t == nil || t.logw == nil { 122 | return 123 | } 124 | prefix := fmt.Sprintf("[%s] ", strings.Join(t.path, "/")) 125 | s := indentf(prefix, pat, vals...) 126 | s = strings.TrimRight(s, " \n") 127 | fmt.Fprintf(t.logw, "%s\n", s) 128 | } 129 | 130 | func (t *tracer) traceUnify(v, w *Value, e nonDetEnv) { 131 | if t == nil { 132 | return 133 | } 134 | 135 | t.logf("Unify\n%s\nwith\n%s\nin\n%s", 136 | yamlf(" ", t.enc.value(v)), 137 | yamlf(" ", t.enc.value(w)), 138 | yamlf(" ", t.enc.env(e))) 139 | 140 | if t.saveTree { 141 | if t.node == nil { 142 | t.node = &traceTree{} 143 | t.trees = append(t.trees, t.node) 144 | } 145 | t.node.v, t.node.w, t.node.envIn = v, w, e 146 | } 147 | } 148 | 149 | func (t *tracer) traceDone(res *Value, e nonDetEnv, err error) { 150 | if t == nil { 151 | return 152 | } 153 | 154 | if err != nil { 155 | t.logf("==> %s", err) 156 | } else { 157 | t.logf("==>\n%s", yamlf(" ", t.enc.closure(Closure{res, e}))) 158 | } 159 | 160 | if t.saveTree { 161 | node := t.node 162 | if node == nil { 163 | panic("popped top of trace stack") 164 | } 165 | node.res, node.err = res, err 166 | node.env = e 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /internal/unify/unify_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "os" 12 | "path/filepath" 13 | "slices" 14 | "strings" 15 | "testing" 16 | 17 | "gopkg.in/yaml.v3" 18 | ) 19 | 20 | func TestUnify(t *testing.T) { 21 | paths, err := filepath.Glob("testdata/*") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | if len(paths) == 0 { 26 | t.Fatal("no testdata found") 27 | } 28 | for _, path := range paths { 29 | // Skip paths starting with _ so experimental files can be added. 30 | base := filepath.Base(path) 31 | if base[0] == '_' { 32 | continue 33 | } 34 | if !strings.HasSuffix(base, ".yaml") { 35 | t.Errorf("non-.yaml file in testdata: %s", base) 36 | continue 37 | } 38 | base = strings.TrimSuffix(base, ".yaml") 39 | 40 | t.Run(base, func(t *testing.T) { 41 | testUnify(t, path) 42 | }) 43 | } 44 | } 45 | 46 | func testUnify(t *testing.T, path string) { 47 | f, err := os.Open(path) 48 | if err != nil { 49 | t.Fatal(err) 50 | } 51 | defer f.Close() 52 | 53 | type testCase struct { 54 | Skip bool 55 | Name string 56 | Unify []Closure 57 | Want yaml.Node 58 | All yaml.Node 59 | } 60 | dec := yaml.NewDecoder(f) 61 | 62 | for i := 0; ; i++ { 63 | var tc testCase 64 | err := dec.Decode(&tc) 65 | if err == io.EOF { 66 | break 67 | } 68 | if err != nil { 69 | t.Fatal(err) 70 | } 71 | 72 | name := tc.Name 73 | if name == "" { 74 | name = fmt.Sprint(i) 75 | } 76 | 77 | t.Run(name, func(t *testing.T) { 78 | if tc.Skip { 79 | t.Skip("skip: true set in test case") 80 | } 81 | 82 | defer func() { 83 | p := recover() 84 | if p != nil || t.Failed() { 85 | // Redo with a trace 86 | // 87 | // TODO: Use t.Output() in Go 1.25. 88 | var buf bytes.Buffer 89 | Debug.UnifyLog = &buf 90 | func() { 91 | defer func() { 92 | // If the original unify panicked, the second one 93 | // probably will, too. Ignore it and let the first panic 94 | // bubble. 95 | recover() 96 | }() 97 | Unify(tc.Unify...) 98 | }() 99 | Debug.UnifyLog = nil 100 | t.Logf("Trace:\n%s", buf.String()) 101 | } 102 | if p != nil { 103 | panic(p) 104 | } 105 | }() 106 | 107 | // Unify the test cases 108 | // 109 | // TODO: Try reordering the inputs also 110 | c, err := Unify(tc.Unify...) 111 | if err != nil { 112 | // TODO: Tests of errors 113 | t.Fatal(err) 114 | } 115 | 116 | // Encode the result back to YAML so we can check if it's structurally 117 | // equal. 118 | clean := func(val any) *yaml.Node { 119 | var node yaml.Node 120 | node.Encode(val) 121 | for n := range allYamlNodes(&node) { 122 | // Canonicalize the style. There may be other style flags we need to 123 | // muck with. 124 | n.Style &^= yaml.FlowStyle 125 | n.HeadComment = "" 126 | n.LineComment = "" 127 | n.FootComment = "" 128 | } 129 | return &node 130 | } 131 | check := func(gotVal any, wantNode *yaml.Node) { 132 | got, err := yaml.Marshal(clean(gotVal)) 133 | if err != nil { 134 | t.Fatalf("Encoding Value back to yaml failed: %s", err) 135 | } 136 | want, err := yaml.Marshal(clean(wantNode)) 137 | if err != nil { 138 | t.Fatalf("Encoding Want back to yaml failed: %s", err) 139 | } 140 | 141 | if !bytes.Equal(got, want) { 142 | t.Errorf("%s:%d:\nwant:\n%sgot\n%s", f.Name(), wantNode.Line, want, got) 143 | } 144 | } 145 | if tc.Want.Kind != 0 { 146 | check(c.val, &tc.Want) 147 | } 148 | if tc.All.Kind != 0 { 149 | fVal := slices.Collect(c.All()) 150 | check(fVal, &tc.All) 151 | } 152 | }) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /internal/unify/value.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import ( 8 | "fmt" 9 | "iter" 10 | "reflect" 11 | ) 12 | 13 | // A Value represents a structured, non-deterministic value consisting of 14 | // strings, tuples of Values, and string-keyed maps of Values. A 15 | // non-deterministic Value will also contain variables, which are resolved via 16 | // an environment as part of a [Closure]. 17 | // 18 | // For debugging, a Value can also track the source position it was read from in 19 | // an input file, and its provenance from other Values. 20 | type Value struct { 21 | Domain Domain 22 | 23 | // A Value has either a pos or parents (or neither). 24 | pos *Pos 25 | parents *[2]*Value 26 | } 27 | 28 | var ( 29 | topValue = &Value{Domain: Top{}} 30 | bottomValue = &Value{Domain: nil} 31 | ) 32 | 33 | // NewValue returns a new [Value] with the given domain and no position 34 | // information. 35 | func NewValue(d Domain) *Value { 36 | return &Value{Domain: d} 37 | } 38 | 39 | // NewValuePos returns a new [Value] with the given domain at position p. 40 | func NewValuePos(d Domain, p Pos) *Value { 41 | return &Value{Domain: d, pos: &p} 42 | } 43 | 44 | // newValueFrom returns a new [Value] with the given domain that copies the 45 | // position information of p. 46 | func newValueFrom(d Domain, p *Value) *Value { 47 | return &Value{Domain: d, pos: p.pos, parents: p.parents} 48 | } 49 | 50 | func unified(d Domain, p1, p2 *Value) *Value { 51 | return &Value{Domain: d, parents: &[2]*Value{p1, p2}} 52 | } 53 | 54 | func (v *Value) Pos() Pos { 55 | if v.pos == nil { 56 | return Pos{} 57 | } 58 | return *v.pos 59 | } 60 | 61 | func (v *Value) PosString() string { 62 | var b []byte 63 | for root := range v.Provenance() { 64 | if len(b) > 0 { 65 | b = append(b, ' ') 66 | } 67 | b, _ = root.pos.AppendText(b) 68 | } 69 | return string(b) 70 | } 71 | 72 | func (v *Value) Exact() bool { 73 | if v.Domain == nil { 74 | return false 75 | } 76 | return v.Domain.Exact() 77 | } 78 | 79 | // Decode decodes v into a Go value. 80 | // 81 | // v must be exact, except that it can include Top. into must be a pointer. 82 | // [Def]s are decoded into structs. [Tuple]s are decoded into slices. [String]s 83 | // are decoded into strings or ints. Any field can itself be a pointer to one of 84 | // these types. Top can be decoded into a pointer-typed field and will set the 85 | // field to nil. Anything else will allocate a value if necessary. 86 | func (v *Value) Decode(into any) error { 87 | rv := reflect.ValueOf(into) 88 | if rv.Kind() != reflect.Pointer { 89 | return fmt.Errorf("cannot decode into non-pointer %T", into) 90 | } 91 | return v.Domain.decode(rv) 92 | } 93 | 94 | func preDecode(rv reflect.Value, kind reflect.Kind, name string) (reflect.Value, error) { 95 | if rv.Kind() == kind { 96 | return rv, nil 97 | } 98 | if rv.Kind() == reflect.Pointer && rv.Type().Elem().Kind() == kind { 99 | if rv.IsNil() { 100 | rv.Set(reflect.New(rv.Type().Elem())) 101 | } 102 | return rv.Elem(), nil 103 | } 104 | return reflect.Value{}, fmt.Errorf("cannot decode %s into %s", name, rv.Type()) 105 | } 106 | 107 | // Provenance iterates over all of the source Values that have contributed to 108 | // this Value. 109 | func (v *Value) Provenance() iter.Seq[*Value] { 110 | return func(yield func(*Value) bool) { 111 | var rec func(d *Value) bool 112 | rec = func(d *Value) bool { 113 | if d.pos != nil { 114 | if !yield(d) { 115 | return false 116 | } 117 | } 118 | if d.parents != nil { 119 | for _, p := range d.parents { 120 | if !rec(p) { 121 | return false 122 | } 123 | } 124 | } 125 | return true 126 | } 127 | rec(v) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /internal/unify/value_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import "slices" 8 | 9 | func ExampleClosure_All_tuple() { 10 | v := mustParse(` 11 | - !sum [1, 2] 12 | - !sum [3, 4] 13 | `) 14 | printYaml(slices.Collect(v.All())) 15 | 16 | // Output: 17 | // - [1, 3] 18 | // - [1, 4] 19 | // - [2, 3] 20 | // - [2, 4] 21 | } 22 | 23 | func ExampleClosure_All_def() { 24 | v := mustParse(` 25 | a: !sum [1, 2] 26 | b: !sum [3, 4] 27 | c: 5 28 | `) 29 | printYaml(slices.Collect(v.All())) 30 | 31 | // Output: 32 | // - {a: 1, b: 3, c: 5} 33 | // - {a: 1, b: 4, c: 5} 34 | // - {a: 2, b: 3, c: 5} 35 | // - {a: 2, b: 4, c: 5} 36 | } 37 | -------------------------------------------------------------------------------- /internal/unify/yaml_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unify 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "iter" 11 | 12 | "gopkg.in/yaml.v3" 13 | ) 14 | 15 | func mustParse(expr string) Closure { 16 | var c Closure 17 | if err := yaml.Unmarshal([]byte(expr), &c); err != nil { 18 | panic(err) 19 | } 20 | return c 21 | } 22 | 23 | func printYaml(val any) { 24 | b, err := yaml.Marshal(val) 25 | if err != nil { 26 | panic(err) 27 | } 28 | var node yaml.Node 29 | if err := yaml.Unmarshal(b, &node); err != nil { 30 | panic(err) 31 | } 32 | 33 | // Map lines to start offsets. We'll use this to figure out when nodes are 34 | // "small" and should use inline style. 35 | lines := []int{-1, 0} 36 | for pos := 0; pos < len(b); { 37 | next := bytes.IndexByte(b[pos:], '\n') 38 | if next == -1 { 39 | break 40 | } 41 | pos += next + 1 42 | lines = append(lines, pos) 43 | } 44 | lines = append(lines, len(b)) 45 | 46 | // Strip comments and switch small nodes to inline style 47 | cleanYaml(&node, lines, len(b)) 48 | 49 | b, err = yaml.Marshal(&node) 50 | if err != nil { 51 | panic(err) 52 | } 53 | fmt.Println(string(b)) 54 | } 55 | 56 | func cleanYaml(node *yaml.Node, lines []int, endPos int) { 57 | node.HeadComment = "" 58 | node.FootComment = "" 59 | node.LineComment = "" 60 | 61 | for i, n2 := range node.Content { 62 | end2 := endPos 63 | if i < len(node.Content)-1 { 64 | end2 = lines[node.Content[i+1].Line] 65 | } 66 | cleanYaml(n2, lines, end2) 67 | } 68 | 69 | // Use inline style? 70 | switch node.Kind { 71 | case yaml.MappingNode, yaml.SequenceNode: 72 | if endPos-lines[node.Line] < 40 { 73 | node.Style = yaml.FlowStyle 74 | } 75 | } 76 | } 77 | 78 | func allYamlNodes(n *yaml.Node) iter.Seq[*yaml.Node] { 79 | return func(yield func(*yaml.Node) bool) { 80 | if !yield(n) { 81 | return 82 | } 83 | for _, n2 := range n.Content { 84 | for n3 := range allYamlNodes(n2) { 85 | if !yield(n3) { 86 | return 87 | } 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /loong64/loong64asm/arg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package loong64asm 6 | 7 | // Naming for Go decoder arguments: 8 | // 9 | // - arg_fd: a Floating Point operand register fd encoded in the fd[4:0] field 10 | // 11 | // - arg_fj: a Floating Point operand register fj encoded in the fj[9:5] field 12 | // 13 | // - arg_fk: a Floating Point operand register fk encoded in the fk[14:10] field 14 | // 15 | // - arg_fa: a Floating Point operand register fa encoded in the fa[19:15] field 16 | // 17 | // - arg_rd: a general-purpose register rd encoded in the rd[4:0] field 18 | // 19 | // - arg_rj: a general-purpose register rj encoded in the rj[9:5] field 20 | // 21 | // - arg_rk: a general-purpose register rk encoded in the rk[14:10] field 22 | // 23 | // - arg_fcsr_4_0: float control status register encoded in [4:0] field 24 | // 25 | // - arg_cd_2_0: condition flag register encoded in [2:0] field 26 | // 27 | // - arg_sa2_16_15: shift bits constant encoded in [16:15] field 28 | // 29 | // - arg_code_14_0: arg for exception process routine encoded in [14:0] field 30 | // 31 | // - arg_ui5_14_10: 5bits unsigned immediate 32 | // 33 | // - arg_lsbw: For details, please refer to chapter 2.2.3.8 of instruction manual 34 | // 35 | // - arg_msbw: For details, please refer to chapter 2.2.3.9 of instruction manual 36 | // 37 | // - arg_hint_4_0: hint field implied the prefetch type and the data should fetch to cache's level 38 | // 0: load to data cache level 1 39 | // 8: store to data cache level 1 40 | // other: no define 41 | // 42 | // - arg_si12_21_10: 12bits signed immediate 43 | 44 | type instArg uint16 45 | 46 | const ( 47 | _ instArg = iota 48 | // 1-5 49 | arg_fd 50 | arg_fj 51 | arg_fk 52 | arg_fa 53 | arg_rd 54 | // 6-10 55 | arg_rj 56 | arg_rk 57 | arg_op_4_0 58 | arg_fcsr_4_0 59 | arg_fcsr_9_5 60 | // 11-15 61 | arg_csr_23_10 62 | arg_cd 63 | arg_cj 64 | arg_ca 65 | arg_sa2_16_15 66 | // 16-20 67 | arg_sa3_17_15 68 | arg_code_4_0 69 | arg_code_14_0 70 | arg_ui5_14_10 71 | arg_ui6_15_10 72 | // 21-25 73 | arg_ui12_21_10 74 | arg_lsbw 75 | arg_msbw 76 | arg_lsbd 77 | arg_msbd 78 | // 26-30 79 | arg_hint_4_0 80 | arg_hint_14_0 81 | arg_level_14_0 82 | arg_level_17_10 83 | arg_seq_17_10 84 | // 31-35 85 | arg_si12_21_10 86 | arg_si14_23_10 87 | arg_si16_25_10 88 | arg_si20_24_5 89 | arg_offset_20_0 90 | // 36~ 91 | arg_offset_25_0 92 | arg_offset_15_0 93 | ) 94 | -------------------------------------------------------------------------------- /loong64/loong64asm/decode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package loong64asm 6 | 7 | import ( 8 | "encoding/hex" 9 | "io/ioutil" 10 | "path/filepath" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | func testDecode(t *testing.T, syntax string) { 16 | input := filepath.Join("testdata", syntax+"cases.txt") 17 | data, err := ioutil.ReadFile(input) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | all := string(data) 22 | for strings.Contains(all, "\t\t") { 23 | all = strings.Replace(all, "\t\t", "\t", -1) 24 | } 25 | for _, line := range strings.Split(all, "\n") { 26 | line = strings.TrimSpace(line) 27 | if line == "" || strings.HasPrefix(line, "#") { 28 | continue 29 | } 30 | f := strings.SplitN(line, "\t", 2) 31 | i := strings.Index(f[0], "|") 32 | if i < 0 { 33 | t.Errorf("parsing %q: missing | separator", f[0]) 34 | continue 35 | } 36 | if i%2 != 0 { 37 | t.Errorf("parsing %q: misaligned | separator", f[0]) 38 | } 39 | code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) 40 | if err != nil { 41 | t.Errorf("parsing %q: %v", f[0], err) 42 | continue 43 | } 44 | asm := f[1] 45 | inst, decodeErr := Decode(code) 46 | if decodeErr != nil && decodeErr != errUnknown { 47 | // Some rarely used system instructions are not supported 48 | // Following logicals will filter such unknown instructions 49 | t.Errorf("parsing %x: %s", code, decodeErr) 50 | continue 51 | } 52 | var out string 53 | switch syntax { 54 | case "gnu": 55 | out = GNUSyntax(inst) 56 | case "plan9": 57 | out = GoSyntax(inst, 0, nil) 58 | default: 59 | t.Errorf("unknown syntax %q", syntax) 60 | continue 61 | } 62 | 63 | // var out string 64 | if asm != out || len(asm) != len(out) { 65 | t.Errorf("Decode(%s) [%s] = %s want %s", f[0], syntax, out, asm) 66 | } 67 | } 68 | } 69 | 70 | func TestDecodeGNUSyntax(t *testing.T) { 71 | testDecode(t, "gnu") 72 | } 73 | 74 | func TestDecodeGoSyntax(t *testing.T) { 75 | testDecode(t, "plan9") 76 | } 77 | -------------------------------------------------------------------------------- /loong64/loong64asm/gnu.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package loong64asm 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. 12 | // This form typically matches the syntax defined in the Loong64 Reference Manual. See 13 | // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html 14 | func GNUSyntax(inst Inst) string { 15 | return strings.ToLower(inst.String()) 16 | } 17 | -------------------------------------------------------------------------------- /loong64/loong64asm/inst.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package loong64asm 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | // An Inst is a single instruction. 13 | type Inst struct { 14 | Op Op // Opcode mnemonic 15 | Enc uint32 // Raw encoding bits. 16 | Args Args // Instruction arguments, in Loong64 manual order. 17 | } 18 | 19 | func (i Inst) String() string { 20 | var op string = i.Op.String() 21 | var args []string 22 | 23 | for _, arg := range i.Args { 24 | if arg == nil { 25 | break 26 | } 27 | args = append(args, arg.String()) 28 | } 29 | 30 | switch i.Op { 31 | case OR: 32 | if i.Args[2].(Reg) == R0 { 33 | op = "move" 34 | args = args[0:2] 35 | } 36 | 37 | case ANDI: 38 | if i.Args[0].(Reg) == R0 && i.Args[1].(Reg) == R0 { 39 | return "nop" 40 | } 41 | 42 | case JIRL: 43 | if i.Args[0].(Reg) == R0 && i.Args[1].(Reg) == R1 && i.Args[2].(OffsetSimm).Imm == 0 { 44 | return "ret" 45 | } else if i.Args[0].(Reg) == R0 && i.Args[2].(OffsetSimm).Imm == 0 { 46 | return "jr " + args[1] 47 | } 48 | 49 | case BLT: 50 | if i.Args[0].(Reg) == R0 { 51 | op = "bgtz" 52 | args = args[1:] 53 | } else if i.Args[1].(Reg) == R0 { 54 | op = "bltz" 55 | args = append(args[:1], args[2:]...) 56 | } 57 | 58 | case BGE: 59 | if i.Args[0].(Reg) == R0 { 60 | op = "blez" 61 | args = args[1:] 62 | } else if i.Args[1].(Reg) == R0 { 63 | op = "bgez" 64 | args = append(args[:1], args[2:]...) 65 | } 66 | } 67 | 68 | if len(args) == 0 { 69 | return op 70 | } else { 71 | return op + " " + strings.Join(args, ", ") 72 | } 73 | } 74 | 75 | // An Op is an Loong64 opcode. 76 | type Op uint16 77 | 78 | // NOTE: The actual Op values are defined in tables.go. 79 | // They are chosen to simplify instruction decoding and 80 | // are not a dense packing from 0 to N, although the 81 | // density is high, probably at least 90%. 82 | func (op Op) String() string { 83 | if (op >= Op(len(opstr))) || (opstr[op] == "") { 84 | return fmt.Sprintf("Op(%d)", int(op)) 85 | } 86 | 87 | return opstr[op] 88 | } 89 | 90 | // An Args holds the instruction arguments. 91 | // If an instruction has fewer than 5 arguments, 92 | // the final elements in the array are nil. 93 | type Args [5]Arg 94 | 95 | // An Arg is a single instruction argument 96 | type Arg interface { 97 | String() string 98 | } 99 | 100 | // A Reg is a single register. 101 | // The zero value denotes R0, not the absence of a register. 102 | type Reg uint16 103 | 104 | const ( 105 | // General-purpose register 106 | R0 Reg = iota 107 | R1 108 | R2 109 | R3 110 | R4 111 | R5 112 | R6 113 | R7 114 | R8 115 | R9 116 | R10 117 | R11 118 | R12 119 | R13 120 | R14 121 | R15 122 | R16 123 | R17 124 | R18 125 | R19 126 | R20 127 | R21 128 | R22 129 | R23 130 | R24 131 | R25 132 | R26 133 | R27 134 | R28 135 | R29 136 | R30 137 | R31 138 | 139 | // Float point register 140 | F0 141 | F1 142 | F2 143 | F3 144 | F4 145 | F5 146 | F6 147 | F7 148 | F8 149 | F9 150 | F10 151 | F11 152 | F12 153 | F13 154 | F14 155 | F15 156 | F16 157 | F17 158 | F18 159 | F19 160 | F20 161 | F21 162 | F22 163 | F23 164 | F24 165 | F25 166 | F26 167 | F27 168 | F28 169 | F29 170 | F30 171 | F31 172 | ) 173 | 174 | func (r Reg) String() string { 175 | switch { 176 | case r == R0: 177 | return "$zero" 178 | 179 | case r == R1: 180 | return "$ra" 181 | 182 | case r == R2: 183 | return "$tp" 184 | 185 | case r == R3: 186 | return "$sp" 187 | 188 | case (r >= R4) && (r <= R11): 189 | return fmt.Sprintf("$a%d", int(r-R4)) 190 | 191 | case (r >= R12) && (r <= R20): 192 | return fmt.Sprintf("$t%d", int(r-R12)) 193 | 194 | case r == R21: 195 | return "$r21" 196 | 197 | case r == R22: 198 | return "$fp" 199 | 200 | case (r >= R23) && (r <= R31): 201 | return fmt.Sprintf("$s%d", int(r-R23)) 202 | 203 | case (r >= F0) && (r <= F7): 204 | return fmt.Sprintf("$fa%d", int(r-F0)) 205 | 206 | case (r >= F8) && (r <= F23): 207 | return fmt.Sprintf("$ft%d", int(r-F8)) 208 | 209 | case (r >= F24) && (r <= F31): 210 | return fmt.Sprintf("$fs%d", int(r-F24)) 211 | 212 | default: 213 | return fmt.Sprintf("Unknown(%d)", int(r)) 214 | } 215 | } 216 | 217 | // float control status register 218 | type Fcsr uint8 219 | 220 | const ( 221 | FCSR0 Fcsr = iota 222 | FCSR1 223 | FCSR2 224 | FCSR3 225 | ) 226 | 227 | func (f Fcsr) String() string { 228 | return fmt.Sprintf("$fcsr%d", uint8(f)) 229 | } 230 | 231 | // float condition flags register 232 | type Fcc uint8 233 | 234 | const ( 235 | FCC0 Fcc = iota 236 | FCC1 237 | FCC2 238 | FCC3 239 | FCC4 240 | FCC5 241 | FCC6 242 | FCC7 243 | ) 244 | 245 | func (f Fcc) String() string { 246 | return fmt.Sprintf("$fcc%d", uint8(f)) 247 | } 248 | 249 | // An Imm is an integer constant. 250 | type Uimm struct { 251 | Imm uint32 252 | Decimal bool 253 | } 254 | 255 | func (i Uimm) String() string { 256 | if i.Decimal == true { 257 | return fmt.Sprintf("%d", i.Imm) 258 | } else { 259 | return fmt.Sprintf("%#x", i.Imm) 260 | } 261 | } 262 | 263 | type Simm16 struct { 264 | Imm int16 265 | Width uint8 266 | } 267 | 268 | func (si Simm16) String() string { 269 | return fmt.Sprintf("%d", int32(si.Imm)) 270 | } 271 | 272 | type Simm32 struct { 273 | Imm int32 274 | Width uint8 275 | } 276 | 277 | func (si Simm32) String() string { 278 | return fmt.Sprintf("%d", int32(si.Imm)) 279 | } 280 | 281 | type OffsetSimm struct { 282 | Imm int32 283 | Width uint8 284 | } 285 | 286 | func (o OffsetSimm) String() string { 287 | return fmt.Sprintf("%d", int32(o.Imm)) 288 | } 289 | 290 | type SaSimm int16 291 | 292 | func (s SaSimm) String() string { 293 | return fmt.Sprintf("%#x", int(s)) 294 | } 295 | 296 | type CodeSimm int16 297 | 298 | func (c CodeSimm) String() string { 299 | return fmt.Sprintf("%#x", int(c)) 300 | } 301 | -------------------------------------------------------------------------------- /loong64/loong64asm/objdump_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package loong64asm 6 | 7 | import ( 8 | "strconv" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestObjdumpLoong64TestDecodeGNUSyntaxdata(t *testing.T) { 14 | testObjdumpLoong64(t, testdataCases(t, "gnu")) 15 | } 16 | 17 | func TestObjdumpLoong64TestDecodeGoSyntaxdata(t *testing.T) { 18 | testObjdumpLoong64(t, testdataCases(t, "plan9")) 19 | } 20 | 21 | func TestObjdumpLoong64Manual(t *testing.T) { 22 | testObjdumpLoong64(t, hexCases(t, objdumpManualTests)) 23 | } 24 | 25 | // objdumpManualTests holds test cases that will be run by TestObjdumpLoong64Manual. 26 | // If you are debugging a few cases that turned up in a longer run, it can be useful 27 | // to list them here and then use -run=Manual, particularly with tracing enabled. 28 | // Note that these are byte sequences, so they must be reversed from the usual 29 | // word presentation. 30 | var objdumpManualTests = ` 31 | 00007238 32 | 00807238 33 | 00004003 34 | 00100050 35 | ac410028 36 | ac41002a 37 | ac41c028 38 | ac414028 39 | ac41402a 40 | ac418028 41 | ac41802a 42 | ac397838 43 | acb97938 44 | acb97838 45 | ac397938 46 | ac397a38 47 | acb97b38 48 | acb97a38 49 | ac397b38 50 | ac110026 51 | ac110024 52 | ac390038 53 | ac392038 54 | ac390c38 55 | ac390438 56 | ac392438 57 | ac390838 58 | ac392838 59 | ac391600 60 | ac391400 61 | ac391500 62 | ac418003 63 | ` 64 | 65 | // allowedMismatchObjdump reports whether the mismatch between text and dec 66 | // should be allowed by the test. 67 | func allowedMismatchObjdump(text string, inst *Inst, dec ExtInst) bool { 68 | // GNU objdump use register, decode use alias of register, so corrected it in here 69 | var dec_text = strings.Replace(dec.text, " ", ",", -1) 70 | var decsp []string = strings.Split(dec_text, ",") 71 | var num int = cap(decsp) 72 | for i := 0; i < num; i++ { 73 | dex := strings.Index(decsp[i], "$r") 74 | fdex := strings.Index(decsp[i], "$f") 75 | ddex := strings.Index(decsp[i], "(") 76 | if ddex > 0 { 77 | // ldptr.w $r12,$r13,16(0x10) 78 | decsp[i] = decsp[i][0:ddex] 79 | } 80 | xdex := strings.Index(decsp[i], "0x") 81 | // convert registers to registers aliases 82 | if dex >= 0 { 83 | reg, _ := strconv.Atoi(decsp[i][dex+2:]) 84 | // r12~r20 $t0~t8 85 | if reg >= 12 && reg <= 20 { 86 | decsp[i] = strings.Join([]string{"t", strconv.Itoa(reg - 12)}, "") 87 | } 88 | // r4~r11 $a0~a7 89 | if reg >= 4 && reg <= 11 { 90 | decsp[i] = strings.Join([]string{"a", strconv.Itoa(reg - 4)}, "") 91 | } 92 | // r23~r31 $s0~s8 93 | if reg >= 23 && reg <= 31 { 94 | decsp[i] = strings.Join([]string{"s", strconv.Itoa(reg - 23)}, "") 95 | } 96 | // r0 zero 97 | if reg == 0 { 98 | decsp[i] = strings.Join([]string{"zero"}, "") 99 | } 100 | // r1 ra 101 | if reg == 1 { 102 | decsp[i] = strings.Join([]string{"ra"}, "") 103 | } 104 | // r2 tp 105 | if reg == 2 { 106 | decsp[i] = strings.Join([]string{"tp"}, "") 107 | } 108 | // r3 sp 109 | if reg == 3 { 110 | decsp[i] = strings.Join([]string{"sp"}, "") 111 | } 112 | // r21 x 113 | if reg == 21 { 114 | decsp[i] = strings.Join([]string{"x"}, "") 115 | } 116 | // r22 fp 117 | if reg == 22 { 118 | decsp[i] = strings.Join([]string{"fp"}, "") 119 | } 120 | } 121 | // convert hexadecimal to decimal 122 | if xdex >= 0 { 123 | parseint, _ := strconv.ParseInt(decsp[i][xdex+2:], 16, 32) 124 | decsp[i] = strings.Join([]string{strconv.Itoa(int(parseint))}, "") 125 | } 126 | // convert floating-point registers to floating-point aliases 127 | if fdex >= 0 && !strings.Contains(decsp[i], "$fcc") { 128 | freg, _ := strconv.Atoi(decsp[i][fdex+2:]) 129 | // f0~f7 fa0~fa7 130 | if freg >= 0 && freg <= 7 { 131 | decsp[i] = strings.Join([]string{"fa", strconv.Itoa(freg - 0)}, "") 132 | } 133 | // f8~f23 ft0~ft15 134 | if freg >= 8 && freg <= 23 { 135 | decsp[i] = strings.Join([]string{"ft", strconv.Itoa(freg - 8)}, "") 136 | } 137 | // f24~f31 fs0~fs7 138 | if freg >= 24 && freg <= 31 { 139 | decsp[i] = strings.Join([]string{"fs", strconv.Itoa(freg - 24)}, "") 140 | } 141 | } 142 | } 143 | 144 | return false 145 | } 146 | -------------------------------------------------------------------------------- /ppc64/ppc64asm/decode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ppc64asm 6 | 7 | import ( 8 | "encoding/binary" 9 | "encoding/hex" 10 | "io/ioutil" 11 | "path" 12 | "strings" 13 | "testing" 14 | ) 15 | 16 | func TestDecode(t *testing.T) { 17 | files, err := ioutil.ReadDir("testdata") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | for _, f := range files { 22 | if !strings.HasPrefix(f.Name(), "decode") { 23 | continue 24 | } 25 | filename := path.Join("testdata", f.Name()) 26 | data, err := ioutil.ReadFile(filename) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | decode(data, t, filename) 31 | } 32 | } 33 | 34 | // Provide a fake symbol to verify PCrel argument decoding. 35 | func symlookup(pc uint64) (string, uint64) { 36 | foopc := uint64(0x100000) 37 | if pc >= foopc && pc < foopc+0x10 { 38 | return "foo", foopc 39 | } 40 | return "", 0 41 | } 42 | 43 | func decode(data []byte, t *testing.T, filename string) { 44 | all := string(data) 45 | // Simulate PC based on number of instructions found in the test file. 46 | pc := uint64(0) 47 | for strings.Contains(all, "\t\t") { 48 | all = strings.Replace(all, "\t\t", "\t", -1) 49 | } 50 | for _, line := range strings.Split(all, "\n") { 51 | line = strings.TrimSpace(line) 52 | if line == "" || strings.HasPrefix(line, "#") { 53 | continue 54 | } 55 | f := strings.SplitN(line, "\t", 3) 56 | i := strings.Index(f[0], "|") 57 | if i < 0 { 58 | t.Errorf("%s: parsing %q: missing | separator", filename, f[0]) 59 | continue 60 | } 61 | if i%2 != 0 { 62 | t.Errorf("%s: parsing %q: misaligned | separator", filename, f[0]) 63 | } 64 | size := i / 2 65 | code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) 66 | if err != nil { 67 | t.Errorf("%s: parsing %q: %v", filename, f[0], err) 68 | continue 69 | } 70 | syntax, asm := f[1], f[2] 71 | inst, err := Decode(code, binary.BigEndian) 72 | var out string 73 | if err != nil { 74 | out = "error: " + err.Error() 75 | } else { 76 | switch syntax { 77 | case "gnu": 78 | out = GNUSyntax(inst, pc) 79 | case "plan9": 80 | pc := pc 81 | // Hack: Setting PC to 0 effectively transforms the PC relative address 82 | // of CALL (bl) into an absolute address when decoding in GoSyntax. This 83 | // simplifies the testing of symbol lookups via symlookup above. 84 | if inst.Op == BL { 85 | pc = 0 86 | } 87 | out = GoSyntax(inst, pc, symlookup) 88 | default: 89 | t.Errorf("unknown syntax %q", syntax) 90 | continue 91 | } 92 | } 93 | pc += uint64(size) 94 | if out != asm || inst.Len != size { 95 | t.Errorf("%s: Decode(%s) [%s] = %s want %s", filename, f[0], syntax, out, asm) 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ppc64/ppc64asm/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package ppc64asm implements decoding of 64-bit PowerPC machine code. 6 | package ppc64asm 7 | -------------------------------------------------------------------------------- /ppc64/ppc64asm/field.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ppc64asm 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | // A BitField is a bit-field in a 32-bit word. 13 | // Bits are counted from 0 from the MSB to 31 as the LSB. 14 | type BitField struct { 15 | Offs uint8 // the offset of the left-most bit. 16 | Bits uint8 // length in bits. 17 | // This instruction word holding this field. 18 | // It is always 0 for ISA < 3.1 instructions. It is 19 | // in decoding order. (0 == prefix, 1 == suffix on ISA 3.1) 20 | Word uint8 21 | } 22 | 23 | func (b BitField) String() string { 24 | if b.Bits > 1 { 25 | return fmt.Sprintf("[%d:%d]", b.Offs, int(b.Offs+b.Bits)-1) 26 | } else if b.Bits == 1 { 27 | return fmt.Sprintf("[%d]", b.Offs) 28 | } else { 29 | return fmt.Sprintf("[%d, len=0]", b.Offs) 30 | } 31 | } 32 | 33 | // Parse extracts the bitfield b from i, and return it as an unsigned integer. 34 | // Parse will panic if b is invalid. 35 | func (b BitField) Parse(i [2]uint32) uint32 { 36 | if b.Bits > 32 || b.Bits == 0 || b.Offs > 31 || b.Offs+b.Bits > 32 { 37 | panic(fmt.Sprintf("invalid bitfiled %v", b)) 38 | } 39 | return (i[b.Word] >> (32 - b.Offs - b.Bits)) & ((1 << b.Bits) - 1) 40 | } 41 | 42 | // ParseSigned extracts the bitfield b from i, and return it as a signed integer. 43 | // ParseSigned will panic if b is invalid. 44 | func (b BitField) ParseSigned(i [2]uint32) int32 { 45 | u := int32(b.Parse(i)) 46 | return u << (32 - b.Bits) >> (32 - b.Bits) 47 | } 48 | 49 | // BitFields is a series of BitFields representing a single number. 50 | type BitFields []BitField 51 | 52 | func (bs BitFields) String() string { 53 | ss := make([]string, len(bs)) 54 | for i, bf := range bs { 55 | ss[i] = bf.String() 56 | } 57 | return fmt.Sprintf("<%s>", strings.Join(ss, "|")) 58 | } 59 | 60 | func (bs *BitFields) Append(b BitField) { 61 | *bs = append(*bs, b) 62 | } 63 | 64 | // parse extracts the bitfields from i, concatenate them and return the result 65 | // as an unsigned integer and the total length of all the bitfields. 66 | // parse will panic if any bitfield in b is invalid, but it doesn't check if 67 | // the sequence of bitfields is reasonable. 68 | func (bs BitFields) parse(i [2]uint32) (u uint64, Bits uint8) { 69 | for _, b := range bs { 70 | u = (u << b.Bits) | uint64(b.Parse(i)) 71 | Bits += b.Bits 72 | } 73 | return u, Bits 74 | } 75 | 76 | // Parse extracts the bitfields from i, concatenate them and return the result 77 | // as an unsigned integer. Parse will panic if any bitfield in b is invalid. 78 | func (bs BitFields) Parse(i [2]uint32) uint64 { 79 | u, _ := bs.parse(i) 80 | return u 81 | } 82 | 83 | // ParseSigned extracts the bitfields from i, concatenate them and return the result 84 | // as a signed integer. Parse will panic if any bitfield in b is invalid. 85 | func (bs BitFields) ParseSigned(i [2]uint32) int64 { 86 | u, l := bs.parse(i) 87 | return int64(u) << (64 - l) >> (64 - l) 88 | } 89 | 90 | // Count the number of bits in the aggregate BitFields 91 | func (bs BitFields) NumBits() int { 92 | num := 0 93 | for _, b := range bs { 94 | num += int(b.Bits) 95 | } 96 | return num 97 | } 98 | -------------------------------------------------------------------------------- /ppc64/ppc64asm/field_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ppc64asm 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func panicOrNot(f func()) (panicked bool) { 12 | defer func() { 13 | if err := recover(); err != nil { 14 | panicked = true 15 | } 16 | }() 17 | f() 18 | return false 19 | } 20 | 21 | func TestBitField(t *testing.T) { 22 | var tests = []struct { 23 | b BitField 24 | i uint32 // input 25 | u uint32 // unsigned output 26 | s int32 // signed output 27 | fail bool // if the check should panic 28 | }{ 29 | {BitField{0, 0, 0}, 0, 0, 0, true}, 30 | {BitField{31, 2, 0}, 0, 0, 0, true}, 31 | {BitField{31, 1, 0}, 1, 1, -1, false}, 32 | {BitField{29, 2, 0}, 0 << 1, 0, 0, false}, 33 | {BitField{29, 2, 0}, 1 << 1, 1, 1, false}, 34 | {BitField{29, 2, 0}, 2 << 1, 2, -2, false}, 35 | {BitField{29, 2, 0}, 3 << 1, 3, -1, false}, 36 | {BitField{0, 32, 0}, 1<<32 - 1, 1<<32 - 1, -1, false}, 37 | {BitField{16, 3, 0}, 1 << 15, 4, -4, false}, 38 | } 39 | for i, tst := range tests { 40 | var ( 41 | ou uint32 42 | os int32 43 | ) 44 | failed := panicOrNot(func() { 45 | ou = tst.b.Parse([2]uint32{tst.i}) 46 | os = tst.b.ParseSigned([2]uint32{tst.i}) 47 | }) 48 | if failed != tst.fail { 49 | t.Errorf("case %d: %v: fail test failed, got %v, expected %v", i, tst.b, failed, tst.fail) 50 | continue 51 | } 52 | if ou != tst.u { 53 | t.Errorf("case %d: %v.Parse(%d) returned %d, expected %d", i, tst.b, tst.i, ou, tst.u) 54 | continue 55 | } 56 | if os != tst.s { 57 | t.Errorf("case %d: %v.ParseSigned(%d) returned %d, expected %d", i, tst.b, tst.i, os, tst.s) 58 | } 59 | } 60 | } 61 | 62 | func TestBitFields(t *testing.T) { 63 | var tests = []struct { 64 | b BitFields 65 | i [2]uint32 // input 66 | u uint64 // unsigned output 67 | s int64 // signed output 68 | nb int // Total number of bits in BitField 69 | fail bool // if the check should panic 70 | }{ 71 | {BitFields{{0, 0, 1}}, [2]uint32{0, 0}, 0, 0, 0, true}, 72 | {BitFields{{31, 2, 1}}, [2]uint32{0, 0}, 0, 0, 2, true}, 73 | {BitFields{{31, 1, 1}}, [2]uint32{0, 1}, 1, -1, 1, false}, 74 | {BitFields{{29, 2, 1}}, [2]uint32{0, 0 << 1}, 0, 0, 2, false}, 75 | {BitFields{{29, 2, 1}}, [2]uint32{0, 1 << 1}, 1, 1, 2, false}, 76 | {BitFields{{29, 2, 1}}, [2]uint32{0, 2 << 1}, 2, -2, 2, false}, 77 | {BitFields{{29, 2, 1}}, [2]uint32{0, 3 << 1}, 3, -1, 2, false}, 78 | {BitFields{{0, 32, 1}}, [2]uint32{0, 1<<32 - 1}, 1<<32 - 1, -1, 32, false}, 79 | {BitFields{{16, 3, 1}}, [2]uint32{0, 1 << 15}, 4, -4, 3, false}, 80 | {BitFields{{16, 16, 0}, {16, 16, 1}}, [2]uint32{0x8016, 0x32}, 0x80160032, -0x7FE9FFCE, 32, false}, 81 | {BitFields{{14, 18, 0}, {16, 16, 1}}, [2]uint32{0x38016, 0x32}, 0x380160032, -0x07FE9FFCE, 34, false}, 82 | } 83 | for i, tst := range tests { 84 | var ( 85 | ou uint64 86 | os int64 87 | onb int 88 | ) 89 | failed := panicOrNot(func() { 90 | onb = tst.b.NumBits() 91 | ou = tst.b.Parse(tst.i) 92 | os = tst.b.ParseSigned(tst.i) 93 | }) 94 | if failed != tst.fail { 95 | t.Errorf("case %d: %v: fail test failed, got %v, expected %v", i, tst.b, failed, tst.fail) 96 | continue 97 | } 98 | if ou != tst.u { 99 | t.Errorf("case %d: %v.Parse(%d) returned %d, expected %d", i, tst.b, tst.i, ou, tst.u) 100 | continue 101 | } 102 | if os != tst.s { 103 | t.Errorf("case %d: %v.ParseSigned(%d) returned %d, expected %d", i, tst.b, tst.i, os, tst.s) 104 | } 105 | if onb != tst.nb { 106 | t.Errorf("case %d: %v.NumBits() returned %d, expected %d", i, tst.b, onb, tst.nb) 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ppc64/ppc64asm/objdump_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ppc64asm 6 | 7 | import ( 8 | "encoding/binary" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestObjdumpPowerTestdata(t *testing.T) { testObjdump(t, testdataCases(t)) } 14 | func TestObjdumpPowerManual(t *testing.T) { testObjdump(t, hexCases(t, objdumpManualTests)) } 15 | 16 | // Disable this for now since generating all possible bit combinations within a word 17 | // generates lots of ppc64x instructions not possible with golang so not worth supporting.. 18 | //func TestObjdumpPowerRandom(t *testing.T) { testObjdump(t, randomCases(t)) } 19 | 20 | // objdumpManualTests holds test cases that will be run by TestObjdumpPowerManual. 21 | // If you are debugging a few cases that turned up in a longer run, it can be useful 22 | // to list them here and then use -run=Manual, particularly with tracing enabled. 23 | // Note that these are byte sequences, so they must be reversed from the usual 24 | // word presentation. 25 | var objdumpManualTests = ` 26 | 6d746162 27 | 4c040000 28 | 88000017 29 | ` 30 | 31 | // allowedMismatchObjdump reports whether the mismatch between text and dec 32 | // should be allowed by the test. 33 | func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool { 34 | // we support more instructions than binutils 35 | if strings.Contains(dec.text, ".long") { 36 | return true 37 | } 38 | 39 | switch inst.Op { 40 | case BC: // We don't print PC relative branches the same way. 41 | return true 42 | case DCBF, DCBT: // We only support extended mnemonics, and may not print 0 where R0 == 0. 43 | return true 44 | case MTVSRWA, MTVSRWZ, MFVSRWZ, MFVSRD, MTVSRD: // We don't support extended mnemonics using VRs or FPRs 45 | return true 46 | case ISEL: // We decode the BI similar to conditional branch insn, objdump doesn't. 47 | return true 48 | case SYNC, WAIT, RFEBB: // ISA 3.1 adds more bits and extended mnemonics for these book ii instructions. 49 | return true 50 | case BL: 51 | // TODO: Ignore these for now. The output format from gnu objdump is dependent on more than the 52 | // instruction itself e.g: decode(48100009) = "bl 0x100008", 4, want "bl .+0x100008", 4 53 | return true 54 | } 55 | 56 | if len(dec.enc) >= 4 { 57 | _ = binary.BigEndian.Uint32(dec.enc[:4]) 58 | } 59 | 60 | return false 61 | } 62 | -------------------------------------------------------------------------------- /ppc64/ppc64util/hack.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // 5 | // This file requires gcc and binutils with -mcpu=power10 support. 6 | // ppc64util runs a series of commands like: 7 | // go run map.go -fmt=asm ../pp64.csv > asm.S 8 | // powerpc64le-linux-gnu-gcc -c asm.S -mcpu=power10 -mbig 9 | // powerpc64le-linux-gnu-objdump -d asm.o 10 | // to create the file decode_generated.txt used to verify the disassembler. 11 | // 12 | // Note, the golang disassembler is not expected to support every extended 13 | // mnemonic, but it should support those which frequently show up in object 14 | // files compiled by the golang toolchain. 15 | 16 | #define RA 1 17 | #define RB 2 18 | #define RS 3 19 | #define RT 4 20 | #define RC 5 21 | #define RSp 6 22 | #define RTp 8 23 | 24 | #define MB 1 25 | #define ME 7 26 | #define NB 2 27 | #define CY 1 28 | 29 | #define LEV 1 30 | 31 | #define FRBp 2 32 | #define FRAp 4 33 | #define FRTp 6 34 | #define FRSp 8 35 | #define FRT 3 36 | #define FRA 5 37 | #define FRB 7 38 | #define FRC 9 39 | #define FRS 11 40 | #define FLM 8 41 | #define U 3 42 | #define W 0 43 | #define TE 15 44 | #define SP 1 45 | #define S 1 46 | #define DRM 0x7 47 | #define RM 0x3 48 | 49 | #define BF 3 50 | #define SH 7 51 | 52 | #define XT 33 53 | #define XA 35 54 | #define XB 37 55 | #define XS 39 56 | #define XC 41 57 | #define XAp 36 58 | #define XTp 38 59 | #define XSp 40 60 | #define DM 1 61 | #define SHW 2 62 | 63 | #define VRA 1 64 | #define VRB 2 65 | #define VRC 3 66 | #define VRT 4 67 | #define VRS 5 68 | #define SHB 3 69 | #define SIX 1 70 | #define ST 1 71 | #define PS 0 72 | #define MP 1 73 | #define bm 0x45FF 74 | #define N 3 75 | 76 | #define AT 7 77 | #define AS 6 78 | 79 | #define RMC 3 80 | 81 | #define UIM 1 82 | #define DCMX 0x23 83 | #define DCM 0x11 84 | #define DGM 0x11 85 | #define R 1 86 | 87 | #define BA 1 88 | #define BB 2 89 | #define BT 3 90 | #define BO 4 91 | #define BI 6 92 | #define BH 0 93 | #define BFA 7 94 | #define FXM 8 95 | #define BC 11 96 | 97 | #define L 1 98 | #define EH 1 99 | 100 | #define SPR 69 101 | #define BHRBE 69 102 | #define TO 0x11 103 | #define TBR 268 104 | #define CT 2 105 | #define FC 2 106 | #define TH 3 107 | #define WC 1 108 | #define PL 0 109 | #define IH 4 110 | #define RIC 1 111 | #define PRS 1 112 | 113 | #define SIM 6 114 | #define IMM 13 115 | #define IMM8 14 116 | #define D 0x80 117 | #define SC 1 118 | 119 | #define target_addr 0x690 120 | 121 | #define XMSK 0x9 122 | #define YMSK 0x3 123 | #define PMSK 0x2 124 | 125 | #define IX 1 126 | #define IMM32 0x1234567 127 | #define Dpfx 0x160032 128 | #define RApfx 0x0 129 | #define Rpfx 1 130 | #define SIpfx 0xFFFFFFFE00010007 131 | 132 | // A valid displacement value for the hash check and hash store instructions. 133 | #define offset -128 134 | 135 | // These decode as m.fpr* or m.vr*. This is a matter of preference. We 136 | // don't support these mnemonics, and I don't think they improve reading 137 | // disassembled code in most cases. so ignore. 138 | // 139 | // Likewise, if you add to this list, add tests to decode.txt to ensure we 140 | // still test these, while ignoring the extended mnemonics which get 141 | // generated. 142 | #define mfvsrd xsrsp 143 | #define mfvsrwz xsrsp 144 | #define mtvsrd xsrsp 145 | #define mtvsrwz xsrsp 146 | #define mtvsrwa xsrsp 147 | 148 | // isel BC bit is not decoded like other BC fields. 149 | // A special test case is added to decode.txt to verify this. 150 | // We decode it like other BC fields. 151 | #define isel rldicl 152 | 153 | 154 | // Likewise, these are obscure book ii instructions with extended mnemonics 155 | // which are almost guaranteed never to show up in go code 156 | #define dcbf add 157 | #define sync xsrsp 158 | #define wait xsrsp 159 | #define rfebb sc 160 | 161 | // sync 1,1 is the stncisync extended mnemonic. Similar to the above, but 162 | // the lwsync/hwsync extended mnemonics are tested in decode.txt 163 | #define sync xsrsp 164 | -------------------------------------------------------------------------------- /ppc64/ppc64util/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // Generate interesting test cases from ppc64 objdump via 8 | // go run util.go 9 | // 10 | // This requires powerpc64le-linux-gnu-gcc and powerpc64le-linux-gnu-objdump be in 11 | // the PATH this command is run. 12 | // 13 | // These tools can be acquired from the IBM advance toolchain for amd64 hosts too. 14 | 15 | package main 16 | 17 | import ( 18 | "bufio" 19 | "fmt" 20 | "io" 21 | "os" 22 | "os/exec" 23 | "regexp" 24 | "strconv" 25 | "strings" 26 | ) 27 | 28 | // Generator for branch on spr (bclr, bctar, bcctr) 29 | func emitBSpr(bo, bi, l uint32, out io.Writer) { 30 | var insn [3]uint32 = [3]uint32{19<<26 | 16<<1, 19<<26 | 528<<1, 19<<26 | 560<<1} 31 | for bh := uint32(0); bh < 3; bh++ { 32 | for _, m := range insn { 33 | m |= bo << 21 34 | m |= bi << 16 35 | m |= bh << 11 36 | m |= l << 0 37 | fmt.Fprintf(out, "\t.long 0x%08x\n", m) 38 | } 39 | } 40 | } 41 | 42 | // Generator for bc 43 | func emitBc(bo, bi, l uint32, out io.Writer) { 44 | for aa := uint32(0); aa < 2; aa++ { 45 | m := uint32(16 << 26) 46 | m |= bo << 21 47 | m |= bi << 16 48 | m |= l << 0 49 | m |= aa << 1 50 | m |= 128 51 | fmt.Fprintf(out, "\t.long 0x%08x\n", m) 52 | } 53 | } 54 | 55 | // Generator all interesting conditional branch type instructions 56 | func emitBranches(out io.Writer) { 57 | fmt.Fprintf(out, ".text\n") 58 | for bo := 0; bo < 0x20; bo++ { 59 | // objdump behaves strangely on some cases when a z bit is set. 60 | // Ignore these, they should never show up in correct code. 61 | if bo&0x15 == 0x1 { 62 | // skip 0b0.0.z cases where z != 0 63 | continue 64 | } 65 | if bo&0x14 == 0x14 && bo != 14 { 66 | // skip 0b1z1zz cases where z != 0 67 | continue 68 | } 69 | // skip at == 1 cases. objdump doesn't handle these well either. 70 | reserved_at := map[int]bool{5: true, 13: true, 17: true, 19: true} 71 | if reserved_at[bo] { 72 | continue 73 | } 74 | // only test cr0/cr1 bits. cr2-cr7 cases are basically identical to cr1. 75 | for bi := 0; bi < 0x8; bi++ { 76 | for l := 0; l < 2; l++ { 77 | emitBSpr(uint32(bo), uint32(bi), uint32(l), out) 78 | emitBc(uint32(bo), uint32(bi), uint32(l), out) 79 | } 80 | } 81 | } 82 | } 83 | 84 | // Emit a test file using the generator called name.txt. This requires 85 | // a GCC toolchain which supports -mcpu=power10. 86 | func genOutput(name, tcPfx string, generator func(io.Writer)) { 87 | // Generate object code from gcc 88 | cmd := exec.Command(tcPfx+"gcc", "-c", "-mbig", "-mcpu=power10", "-x", "assembler-with-cpp", "-o", name+".o", "-") 89 | input, _ := cmd.StdinPipe() 90 | cmd.Stderr = os.Stderr 91 | go func() { 92 | defer input.Close() 93 | generator(input.(io.Writer)) 94 | }() 95 | if cmd.Run() != nil { 96 | fmt.Printf("Failed running gcc for: %s\n", name) 97 | return 98 | } 99 | defer os.Remove(name + ".o") 100 | cmd = exec.Command(tcPfx+"objdump", "-d", name+".o") 101 | 102 | // Run objdump and parse output into test format 103 | output, _ := cmd.StdoutPipe() 104 | defer output.Close() 105 | scanner := bufio.NewScanner(output) 106 | spacere := regexp.MustCompile("[[:space:]]+") 107 | outf, _ := os.Create(name + ".txt") 108 | defer outf.Close() 109 | if cmd.Start() != nil { 110 | fmt.Printf("Failed running objdump for: %s\n", name) 111 | return 112 | } 113 | 114 | pfx := "" 115 | dec := "" 116 | for scanner.Scan() { 117 | ln := spacere.Split(scanner.Text(), -1) 118 | if len(ln) >= 7 { 119 | opc := strings.Join(ln[2:6], "") 120 | if len(pfx) == 0 { 121 | dec = strings.Join(ln[6:], " ") 122 | } 123 | if v, _ := strconv.ParseInt(ln[2], 16, 16); v&0xFC == 0x04 { 124 | pfx = opc 125 | continue 126 | } 127 | fmt.Fprintf(outf, "%s%s|\tgnu\t%s\n", pfx, opc, dec) 128 | pfx = "" 129 | } 130 | 131 | } 132 | cmd.Wait() 133 | } 134 | 135 | // Generate representative instructions for all[1] instructions in pp64.csv. 136 | // 137 | // [1] See hack.h for a few minor, exceptional workarounds. 138 | func emitGenerated(out io.Writer) { 139 | cmd := exec.Command("go", "run", "../ppc64map/map.go", "-fmt=asm", "../pp64.csv") 140 | cmdout, _ := cmd.Output() 141 | out.Write(cmdout) 142 | } 143 | 144 | // Produce generated test outputs. This should be run every so often with 145 | // new versions of objdump to ensure we stay up to date. 146 | func main() { 147 | genOutput("decode_branch", "powerpc64le-linux-gnu-", emitBranches) 148 | genOutput("decode_generated", "powerpc64le-linux-gnu-", emitGenerated) 149 | } 150 | -------------------------------------------------------------------------------- /riscv64/riscv64asm/arg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package riscv64asm 6 | 7 | // Naming for Go decoder arguments: 8 | // 9 | // - arg_rd: a general purpose register rd encoded in rd[11:7] field 10 | // 11 | // - arg_rs1: a general purpose register rs1 encoded in rs1[19:15] field 12 | // 13 | // - arg_rs2: a general purpose register rs2 encoded in rs2[24:20] field 14 | // 15 | // - arg_rs3: a general purpose register rs3 encoded in rs3[31:27] field 16 | // 17 | // - arg_fd: a floating point register rd encoded in rd[11:7] field 18 | // 19 | // - arg_fs1: a floating point register rs1 encoded in rs1[19:15] field 20 | // 21 | // - arg_fs2: a floating point register rs2 encoded in rs2[24:20] field 22 | // 23 | // - arg_fs3: a floating point register rs3 encoded in rs3[31:27] field 24 | // 25 | // - arg_csr: a control status register encoded in csr[31:20] field 26 | // 27 | // - arg_rs1_mem: source register with offset in load commands 28 | // 29 | // - arg_rs1_store: source register with offset in store commands 30 | // 31 | // - arg_rs1_amo: source register with offset in atomic commands 32 | // 33 | // - arg_pred: predecessor memory ordering information encoded in pred[27:24] field 34 | // For details, please refer to chapter 2.7 of ISA manual volume 1 35 | // 36 | // - arg_succ: successor memory ordering information encoded in succ[23:20] field 37 | // For details, please refer to chapter 2.7 of ISA manual volume 1 38 | // 39 | // - arg_zimm: a unsigned immediate encoded in zimm[19:15] field 40 | // 41 | // - arg_imm12: an I-type immediate encoded in imm12[31:20] field 42 | // 43 | // - arg_simm12: a S-type immediate encoded in simm12[31:25|11:7] field 44 | // 45 | // - arg_bimm12: a B-type immediate encoded in bimm12[31:25|11:7] field 46 | // 47 | // - arg_imm20: an U-type immediate encoded in imm20[31:12] field 48 | // 49 | // - arg_jimm20: a J-type immediate encoded in jimm20[31:12] field 50 | // 51 | // - arg_shamt5: a shift amount encoded in shamt5[24:20] field 52 | // 53 | // - arg_shamt6: a shift amount encoded in shamt6[25:20] field 54 | // 55 | 56 | type argType uint16 57 | 58 | const ( 59 | _ argType = iota 60 | arg_rd 61 | arg_rs1 62 | arg_rs2 63 | arg_rs3 64 | arg_fd 65 | arg_fs1 66 | arg_fs2 67 | arg_fs3 68 | arg_csr 69 | 70 | arg_rs1_amo 71 | arg_rs1_mem 72 | arg_rs1_store 73 | 74 | arg_pred 75 | arg_succ 76 | 77 | arg_zimm 78 | arg_imm12 79 | arg_simm12 80 | arg_bimm12 81 | arg_imm20 82 | arg_jimm20 83 | arg_shamt5 84 | arg_shamt6 85 | 86 | // RISC-V Compressed Extension Args 87 | arg_rd_p 88 | arg_fd_p 89 | arg_rs1_p 90 | arg_rd_rs1_p 91 | arg_fs2_p 92 | arg_rs2_p 93 | arg_rd_n0 94 | arg_rs1_n0 95 | arg_rd_rs1_n0 96 | arg_c_rs1_n0 97 | arg_c_rs2_n0 98 | arg_c_fs2 99 | arg_c_rs2 100 | arg_rd_n2 101 | 102 | arg_c_imm6 103 | arg_c_nzimm6 104 | arg_c_nzuimm6 105 | arg_c_uimm7 106 | arg_c_uimm8 107 | arg_c_uimm8sp_s 108 | arg_c_uimm8sp 109 | arg_c_uimm9sp_s 110 | arg_c_uimm9sp 111 | arg_c_bimm9 112 | arg_c_nzimm10 113 | arg_c_nzuimm10 114 | arg_c_imm12 115 | arg_c_nzimm18 116 | ) 117 | -------------------------------------------------------------------------------- /riscv64/riscv64asm/decode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package riscv64asm 6 | 7 | import ( 8 | "bufio" 9 | "encoding/hex" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | "testing" 14 | ) 15 | 16 | func testDecode(t *testing.T, syntax string) { 17 | input := filepath.Join("testdata", syntax+"cases.txt") 18 | f, err := os.Open(input) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | defer f.Close() 23 | scanner := bufio.NewScanner(f) 24 | for scanner.Scan() { 25 | line := strings.TrimSpace(scanner.Text()) 26 | if line == "" || strings.HasPrefix(line, "#") { 27 | continue 28 | } 29 | f := strings.SplitN(line, "\t", 2) 30 | i := strings.Index(f[0], "|") 31 | 32 | if i < 0 { 33 | t.Errorf("parsing %q: missing | separator", f[0]) 34 | continue 35 | } 36 | if i%2 != 0 { 37 | t.Errorf("parsing %q: misaligned | separator", f[0]) 38 | } 39 | code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) 40 | if err != nil { 41 | t.Errorf("parsing %q: %v", f[0], err) 42 | continue 43 | } 44 | asm0 := strings.Replace(f[1], " ", " ", -1) 45 | asm := strings.TrimSpace(asm0) 46 | inst, decodeErr := Decode(code) 47 | if decodeErr != nil && decodeErr != errUnknown { 48 | if asm == "illegalins" && decodeErr == errShort { 49 | continue 50 | } 51 | // Some rarely used system instructions are not supported 52 | // Following logicals will filter such unknown instructions 53 | t.Errorf("parsing %x: %s", code, decodeErr) 54 | continue 55 | } 56 | 57 | var out string 58 | switch syntax { 59 | case "gnu": 60 | out = GNUSyntax(inst) 61 | case "plan9": 62 | out = GoSyntax(inst, 0, nil, nil) 63 | default: 64 | t.Errorf("unknown syntax %q", syntax) 65 | continue 66 | } 67 | 68 | if asm != out { 69 | t.Errorf("Decode(%s) [%s] = %s want %s", f[0], syntax, out, asm) 70 | } 71 | } 72 | } 73 | 74 | func TestDecodeGNUSyntax(t *testing.T) { 75 | testDecode(t, "gnu") 76 | } 77 | 78 | func TestDecodeGoSyntax(t *testing.T) { 79 | testDecode(t, "plan9") 80 | } 81 | -------------------------------------------------------------------------------- /riscv64/riscv64asm/objdump_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package riscv64asm 6 | 7 | import ( 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestObjdumpRISCV64TestDecodeGNUSyntaxdata(t *testing.T) { 13 | testObjdumpRISCV64(t, testdataCases(t, "gnu")) 14 | } 15 | func TestObjdumpRISCV64TestDecodeGoSyntaxdata(t *testing.T) { 16 | testObjdumpRISCV64(t, testdataCases(t, "plan9")) 17 | } 18 | 19 | func TestObjdumpRISCV64Manual(t *testing.T) { 20 | testObjdumpRISCV64(t, hexCases(t, objdumpManualTests)) 21 | } 22 | 23 | // objdumpManualTests holds test cases that will be run by TestObjdumpRISCV64Manual. 24 | // If you are debugging a few cases that turned up in a longer run, it can be useful 25 | // to list them here and then use -run=Manual, particularly with tracing enabled. 26 | // Note that these are byte sequences, so they must be reversed from the usual 27 | // word presentation. 28 | var objdumpManualTests = ` 29 | 93020300 30 | 13000000 31 | 9b020300 32 | afb5b50e 33 | 73b012c0 34 | 73f01fc0 35 | 73a012c0 36 | 73e01fc0 37 | f3223000 38 | f3221000 39 | f3222000 40 | f3123300 41 | f3121300 42 | f3122300 43 | 739012c0 44 | 73d01fc0 45 | 53a01022 46 | 53a01020 47 | 53801022 48 | 53801020 49 | 53901022 50 | 53901020 51 | 67800000 52 | 67800200 53 | b3026040 54 | bb026040 55 | 9342f3ff 56 | f32200c0 57 | f32200c8 58 | f32220c0 59 | f32220c8 60 | f32210c0 61 | f32210c8 62 | ` 63 | 64 | // allowedMismatchObjdump reports whether the mismatch between text and dec 65 | // should be allowed by the test. 66 | func allowedMismatchObjdump(text string, inst *Inst, dec ExtInst) bool { 67 | // Allow the mismatch of Branch/Jump instruction's offset. 68 | decsp := strings.Split(dec.text, ",") 69 | 70 | switch inst.Op { 71 | case BEQ, BGE, BGEU, BLT, BLTU, BNE: 72 | if inst.Args[2].(Simm).String() != decsp[len(decsp)-1] { 73 | return true 74 | } 75 | case JAL: 76 | if inst.Args[1].(Simm).String() != decsp[len(decsp)-1] { 77 | return true 78 | } 79 | case JALR: 80 | if inst.Args[1].(RegOffset).Ofs.String() != decsp[len(decsp)-1] { 81 | return true 82 | } 83 | } 84 | 85 | return false 86 | } 87 | -------------------------------------------------------------------------------- /riscv64/riscv64asm/testdata/Makefile: -------------------------------------------------------------------------------- 1 | go test command: 2 | cd ..; go generate 3 | cd ..; go test -run 'ObjdumpRISCV64TestGUNSyntaxdata' -v -timeout 10h 2>&1 | tee -a log 4 | cd ..; go test -run 'ObjdumpRISCV64TestGoSyntaxdata' -v -timeout 10h 2>&1 | tee -a log 5 | cd ..; go test -run 'ObjdumpRISCV64' -v -timeout 10h 2>&1 | tee -a log 6 | cd ..; go test -run 'ObjdumpRISCV64Manual' -v -timeout 10h 2>&1 | tee -a log 7 | cd ..; go test -run 'TestDecodeGNUSyntax' 8 | cd ..; go test -run 'TestDecodeGoSyntax' 9 | cd ..; go test -run '.*' 10 | -------------------------------------------------------------------------------- /s390x/s390xasm/Makefile: -------------------------------------------------------------------------------- 1 | tables.go: ../s390xmap/map.go ../s390x.csv 2 | go run ../s390xmap/map.go -fmt=decoder ../s390x.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go 3 | -------------------------------------------------------------------------------- /s390x/s390xasm/decode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package s390xasm 6 | 7 | import ( 8 | "encoding/hex" 9 | "io/ioutil" 10 | "path" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | func TestDecode(t *testing.T) { 16 | files, err := ioutil.ReadDir("testdata") 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | for _, f := range files { 21 | if !strings.HasPrefix(f.Name(), "decode") { 22 | continue 23 | } 24 | filename := path.Join("testdata", f.Name()) 25 | data, err := ioutil.ReadFile(filename) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | decode(data, t, filename) 30 | } 31 | } 32 | 33 | func decode(data []byte, t *testing.T, filename string) { 34 | all := string(data) 35 | // Simulate PC based on number of instructions found in the test file. 36 | pc := uint64(0) 37 | for strings.Contains(all, "\t\t") { 38 | all = strings.Replace(all, "\t\t", "\t", -1) 39 | } 40 | for _, line := range strings.Split(all, "\n") { 41 | line = strings.TrimSpace(line) 42 | if line == "" || strings.HasPrefix(line, "#") { 43 | continue 44 | } 45 | f := strings.SplitN(line, "\t", 3) 46 | i := strings.Index(f[0], "|") 47 | if i < 0 { 48 | t.Errorf("%s: parsing %q: missing | separator", filename, f[0]) 49 | continue 50 | } 51 | if i%2 != 0 { 52 | t.Errorf("%s: parsing %q: misaligned | separator", filename, f[0]) 53 | } 54 | size := i / 2 55 | code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) 56 | if err != nil { 57 | t.Errorf("%s: parsing %q: %v", filename, f[0], err) 58 | continue 59 | } 60 | syntax, asm := f[1], f[2] 61 | inst, err := Decode(code) 62 | var out string 63 | if err != nil { 64 | out = "error: " + err.Error() 65 | } else { 66 | switch syntax { 67 | case "gnu": 68 | out = GNUSyntax(inst, pc) 69 | case "plan9": 70 | out = GoSyntax(inst, pc, nil) 71 | default: 72 | t.Errorf("unknown syntax %q", syntax) 73 | continue 74 | } 75 | } 76 | pc += uint64(size) 77 | if out != asm || inst.Len != size { 78 | t.Errorf("%s: Decode(%s) [%s] = %s want %s", filename, f[0], syntax, out, asm) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /s390x/s390xasm/field.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package s390xasm 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | // A BitField is a bit-field in a 64-bit double word. 12 | // Bits are counted from 0 from the MSB to 63 as the LSB. 13 | type BitField struct { 14 | Offs uint8 // the offset of the left-most bit. 15 | Bits uint8 // length in bits. 16 | } 17 | 18 | func (b BitField) String() string { 19 | if b.Bits > 1 { 20 | return fmt.Sprintf("[%d:%d]", b.Offs, int(b.Offs+b.Bits)-1) 21 | } else if b.Bits == 1 { 22 | return fmt.Sprintf("[%d]", b.Offs) 23 | } else { 24 | return fmt.Sprintf("[%d, len=0]", b.Offs) 25 | } 26 | } 27 | 28 | // Parse extracts the bitfield b from i, and return it as an unsigned integer. 29 | // Parse will panic if b is invalid. 30 | func (b BitField) Parse(i uint64) uint64 { 31 | if b.Bits > 64 || b.Bits == 0 || b.Offs > 63 || b.Offs+b.Bits > 64 { 32 | panic(fmt.Sprintf("invalid bitfiled %v", b)) 33 | } 34 | if b.Bits == 20 { 35 | return ((((i >> (64 - b.Offs - b.Bits)) & ((1 << 8) - 1)) << 12) | ((i >> (64 - b.Offs - b.Bits + 8)) & 0xFFF)) 36 | 37 | } else { 38 | return (i >> (64 - b.Offs - b.Bits)) & ((1 << b.Bits) - 1) 39 | } 40 | } 41 | 42 | // ParseSigned extracts the bitfield b from i, and return it as a signed integer. 43 | // ParseSigned will panic if b is invalid. 44 | func (b BitField) ParseSigned(i uint64) int64 { 45 | u := int64(b.Parse(i)) 46 | return u << (64 - b.Bits) >> (64 - b.Bits) 47 | } 48 | -------------------------------------------------------------------------------- /s390x/s390xutil/hack.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // 5 | // This file requires gcc and binutils with -march=z16 support. 6 | // s390xutil runs a series of commands like: 7 | // go run map.go -fmt=asm ../s390x.csv > asm.S 8 | // /usr/bin/gcc -c asm.S -march=z16 9 | // /usr/bin/objdump -d asm.o 10 | // to create the file decode_generated.txt used to verify the disassembler. 11 | // 12 | // Note, the Go disassembler is not expected to support every extended 13 | // mnemonic, but it should support those which frequently show up in object 14 | // files compiled by the Go toolchain. 15 | 16 | 17 | #define R1 8 18 | #define R2 0 19 | #define R3 0 20 | 21 | #define X2 2 22 | 23 | #define L1 4 24 | #define L2 4 25 | 26 | #define B1 2 27 | #define B2 1 28 | #define B3 6 29 | #define B4 8 30 | 31 | #define D1 6 32 | #define D2 11 33 | #define D3 182 34 | #define D4 205 35 | 36 | #define V1 18 37 | #define V2 3 38 | #define V3 5 39 | #define V4 8 40 | 41 | #define I 124 42 | #define I1 12 43 | #define I2 8 44 | #define I3 9 45 | #define I4 105 46 | #define I5 18 47 | 48 | #define RI2 0 49 | #define RI3 294 50 | #define RI4 -168 51 | 52 | #define M1 7 53 | #define M3 3 54 | #define M4 1 55 | #define M5 9 56 | #define M6 11 57 | -------------------------------------------------------------------------------- /s390x/s390xutil/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // Generate interesting test cases from s390x objdump via 8 | // go run util.go 9 | // 10 | // This requires "/usr/bin/gcc" and "objdump" be in the PATH this command is run. 11 | // 12 | // These tools can be acquired from the IBM advance toolchain for amd64 hosts too. 13 | 14 | package main 15 | 16 | import ( 17 | "bufio" 18 | "fmt" 19 | "io" 20 | "os" 21 | "os/exec" 22 | "regexp" 23 | "strconv" 24 | "strings" 25 | ) 26 | 27 | // Emit a test file using the generator called name.txt. This requires 28 | // a GCC toolchain which supports -march=z16. 29 | func genOutput(name, tcPfx string, generator func(io.Writer)) { 30 | // Generate object code from gcc 31 | cmd := exec.Command(tcPfx+"gcc", "-c", "-march=z16", "-x", "assembler-with-cpp", "-o", name+".o", "-") 32 | input, _ := cmd.StdinPipe() 33 | cmd.Stderr = os.Stderr 34 | go func() { 35 | defer input.Close() 36 | generator(input.(io.Writer)) 37 | }() 38 | if cmd.Run() != nil { 39 | fmt.Printf("Failed running gcc for: %s\n", name) 40 | return 41 | } 42 | defer os.Remove(name + ".o") 43 | cmd = exec.Command(tcPfx+"objdump", "-d", name+".o") 44 | 45 | // Run objdump and parse output into test format 46 | output, _ := cmd.StdoutPipe() 47 | defer output.Close() 48 | scanner := bufio.NewScanner(output) 49 | spacere := regexp.MustCompile("[[:space:]]+") 50 | outf, _ := os.Create(name + ".txt") 51 | defer outf.Close() 52 | if cmd.Start() != nil { 53 | fmt.Printf("Failed running objdump for: %s\n", name) 54 | return 55 | } 56 | 57 | for scanner.Scan() { 58 | ln := spacere.Split(scanner.Text(), -1) 59 | var cnt int16 60 | if len(ln) >= 5 { 61 | v, _ := strconv.ParseInt(ln[2], 16, 16) 62 | if (v >> 6 & 0x3) == 0 { 63 | cnt = 2 64 | } else if v>>6&0x3 == 1 || v>>6&0x3 == 2 { 65 | cnt = 4 66 | } else { 67 | cnt = 6 68 | } 69 | opc := strings.Join(ln[2:cnt+2], "") 70 | dec := strings.Join(ln[cnt+2:], " ") 71 | fmt.Fprintf(outf, "%12s|\tgnu\t%-18s\n", opc, dec) 72 | } 73 | } 74 | cmd.Wait() 75 | } 76 | 77 | // Generate representative instructions for all[1] instructions in s390x.csv. 78 | // 79 | // [1] See hack.h for a few minor, exceptional workarounds. 80 | func emitGenerated(out io.Writer) { 81 | cmd := exec.Command("go", "run", "../s390xmap/map.go", "-fmt=asm", "../s390x.csv") 82 | cmdout, _ := cmd.Output() 83 | out.Write(cmdout) 84 | } 85 | 86 | // Produce generated test outputs. This should be run every so often with 87 | // new versions of objdump to ensure we stay up to date. 88 | func main() { 89 | genOutput("decode_generated", "/usr/bin/", emitGenerated) 90 | } 91 | -------------------------------------------------------------------------------- /x86/x86asm/Makefile: -------------------------------------------------------------------------------- 1 | tables.go: ../x86map/map.go ../x86.csv 2 | go run ../x86map/map.go -fmt=decoder ../x86.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go 3 | 4 | -------------------------------------------------------------------------------- /x86/x86asm/decode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x86asm 6 | 7 | import ( 8 | "encoding/hex" 9 | "io/ioutil" 10 | "strconv" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | func TestDecode(t *testing.T) { 16 | data, err := ioutil.ReadFile("testdata/decode.txt") 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | all := string(data) 21 | for strings.Contains(all, "\t\t") { 22 | all = strings.Replace(all, "\t\t", "\t", -1) 23 | } 24 | for _, line := range strings.Split(all, "\n") { 25 | line = strings.TrimSpace(line) 26 | if line == "" || strings.HasPrefix(line, "#") { 27 | continue 28 | } 29 | f := strings.SplitN(line, "\t", 4) 30 | i := strings.Index(f[0], "|") 31 | if i < 0 { 32 | t.Errorf("parsing %q: missing | separator", f[0]) 33 | continue 34 | } 35 | if i%2 != 0 { 36 | t.Errorf("parsing %q: misaligned | separator", f[0]) 37 | } 38 | size := i / 2 39 | code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) 40 | if err != nil { 41 | t.Errorf("parsing %q: %v", f[0], err) 42 | continue 43 | } 44 | mode, err := strconv.Atoi(f[1]) 45 | if err != nil { 46 | t.Errorf("invalid mode %q in: %s", f[1], line) 47 | continue 48 | } 49 | syntax, asm := f[2], f[3] 50 | inst, err := Decode(code, mode) 51 | var out string 52 | if err != nil { 53 | out = "error: " + err.Error() 54 | } else { 55 | switch syntax { 56 | case "gnu": 57 | out = GNUSyntax(inst, 0, nil) 58 | case "intel": 59 | out = IntelSyntax(inst, 0, nil) 60 | case "plan9": // [sic] 61 | out = GoSyntax(inst, 0, nil) 62 | default: 63 | t.Errorf("unknown syntax %q", syntax) 64 | continue 65 | } 66 | } 67 | if out != asm || inst.Len != size { 68 | t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size) 69 | } 70 | } 71 | } 72 | 73 | func TestDecodeDoesNotCrash(t *testing.T) { 74 | cases := [...][]byte{ 75 | []byte{}, 76 | []byte{0xc5}, 77 | []byte{0xc4}, 78 | } 79 | for _, test := range cases { 80 | _, err := Decode([]byte(test), 64) // the only goal is that this line does not panic 81 | if err == nil { 82 | t.Errorf("expected error on invalid instruction %x", test) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /x86/x86asm/format_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x86asm 6 | 7 | import ( 8 | "encoding/hex" 9 | "testing" 10 | ) 11 | 12 | func testFormattingSymname(addr uint64) (string, uint64) { 13 | switch addr { 14 | case 0x424080: 15 | return "runtime.printint", 0x424080 16 | case 0x4c8068: 17 | return "main.A", 0x4c8068 18 | } 19 | return "", 0 20 | } 21 | 22 | func TestFormatting(t *testing.T) { 23 | testCases := []struct { 24 | PC uint64 25 | bytes string 26 | 27 | goSyntax, intelSyntax, gnuSyntax string 28 | }{ 29 | {0x4816b2, "0f8677010000", 30 | "JBE 0x48182f", 31 | "jbe 0x48182f", 32 | "jbe 0x48182f"}, 33 | {0x45065b, "488b442408", 34 | "MOVQ 0x8(SP), AX", 35 | "mov rax, qword ptr [rsp+0x8]", 36 | "mov 0x8(%rsp),%rax"}, 37 | {0x450678, "488b05e9790700", 38 | "MOVQ main.A(SB), AX", 39 | "mov rax, qword ptr [main.A]", 40 | "mov main.A,%rax"}, 41 | {0x450664, "e8173afdff", 42 | "CALL runtime.printint(SB)", 43 | "call runtime.printint", 44 | "callq runtime.printint"}, 45 | {0x45069b, "488d0575d90100", 46 | "LEAQ 0x1d975(IP), AX", 47 | "lea rax, ptr [rip+0x1d975]", 48 | "lea 0x1d975(%rip),%rax"}, 49 | } 50 | 51 | for _, testCase := range testCases { 52 | t.Logf("%#x %s %s", testCase.PC, testCase.bytes, testCase.goSyntax) 53 | bs, _ := hex.DecodeString(testCase.bytes) 54 | inst, err := Decode(bs, 64) 55 | if err != nil { 56 | t.Errorf("decode error %v", err) 57 | } 58 | if out := GoSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.goSyntax { 59 | t.Errorf("GoSyntax: %q", out) 60 | } 61 | if out := IntelSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.intelSyntax { 62 | t.Errorf("IntelSyntax: %q expected: %q", out, testCase.intelSyntax) 63 | } 64 | if out := GNUSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.gnuSyntax { 65 | t.Errorf("GNUSyntax: %q expected: %q", out, testCase.gnuSyntax) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /x86/x86asm/inst_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x86asm 6 | 7 | import ( 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestRegString(t *testing.T) { 13 | for r := Reg(1); r <= regMax; r++ { 14 | if regNames[r] == "" { 15 | t.Errorf("regNames[%d] is missing", int(r)) 16 | } else if s := r.String(); strings.Contains(s, "Reg(") { 17 | t.Errorf("Reg(%d).String() = %s, want proper name", int(r), s) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /x86/x86asm/plan9ext_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x86asm 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "log" 12 | "os" 13 | "strconv" 14 | "testing" 15 | ) 16 | 17 | const plan9Path = "testdata/libmach8db" 18 | 19 | func testPlan9Arch(t *testing.T, arch int, generate func(func([]byte))) { 20 | if testing.Short() { 21 | t.Skip("skipping libmach test in short mode") 22 | } 23 | if _, err := os.Stat(plan9Path); err != nil { 24 | t.Skip(err) 25 | } 26 | 27 | testExtDis(t, "plan9", arch, plan9, generate, allowedMismatchPlan9) 28 | } 29 | 30 | func testPlan932(t *testing.T, generate func(func([]byte))) { 31 | testPlan9Arch(t, 32, generate) 32 | } 33 | 34 | func testPlan964(t *testing.T, generate func(func([]byte))) { 35 | testPlan9Arch(t, 64, generate) 36 | } 37 | 38 | func plan9(ext *ExtDis) error { 39 | flag := "-8" 40 | if ext.Arch == 64 { 41 | flag = "-6" 42 | } 43 | b, err := ext.Run(plan9Path, flag, ext.File.Name()) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | nmatch := 0 49 | next := uint32(start) 50 | var ( 51 | addr uint32 52 | encbuf [32]byte 53 | enc []byte 54 | text string 55 | ) 56 | 57 | for { 58 | line, err := b.ReadSlice('\n') 59 | if err != nil { 60 | if err == io.EOF { 61 | break 62 | } 63 | return fmt.Errorf("reading libmach8db output: %v", err) 64 | } 65 | if debug { 66 | os.Stdout.Write(line) 67 | } 68 | nmatch++ 69 | addr, enc, text = parseLinePlan9(line, encbuf[:0]) 70 | if addr > next { 71 | return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line) 72 | } 73 | if addr < next { 74 | continue 75 | } 76 | if m := pcrelw.FindStringSubmatch(text); m != nil { 77 | targ, _ := strconv.ParseUint(m[2], 16, 64) 78 | text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc)))) 79 | } 80 | if m := pcrel.FindStringSubmatch(text); m != nil { 81 | targ, _ := strconv.ParseUint(m[2], 16, 64) 82 | text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc)))) 83 | } 84 | ext.Dec <- ExtInst{addr, encbuf, len(enc), text} 85 | encbuf = [32]byte{} 86 | enc = nil 87 | next += 32 88 | } 89 | if next != start+uint32(ext.Size) { 90 | return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size) 91 | } 92 | if err := ext.Wait(); err != nil { 93 | return fmt.Errorf("exec: %v", err) 94 | } 95 | 96 | return nil 97 | } 98 | 99 | func parseLinePlan9(line []byte, encstart []byte) (addr uint32, enc []byte, text string) { 100 | i := bytes.IndexByte(line, ' ') 101 | if i < 0 || line[0] != '0' || line[1] != 'x' { 102 | log.Fatalf("cannot parse disassembly: %q", line) 103 | } 104 | j := bytes.IndexByte(line[i+1:], ' ') 105 | if j < 0 { 106 | log.Fatalf("cannot parse disassembly: %q", line) 107 | } 108 | j += i + 1 109 | x, err := strconv.ParseUint(string(trimSpace(line[2:i])), 16, 32) 110 | if err != nil { 111 | log.Fatalf("cannot parse disassembly: %q", line) 112 | } 113 | addr = uint32(x) 114 | enc, ok := parseHex(line[i+1:j], encstart) 115 | if !ok { 116 | log.Fatalf("cannot parse disassembly: %q", line) 117 | } 118 | return addr, enc, string(fixSpace(line[j+1:])) 119 | } 120 | -------------------------------------------------------------------------------- /x86/x86asm/plan9x_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x86asm 6 | 7 | import ( 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestPlan932Manual(t *testing.T) { testPlan932(t, hexCases(t, plan9ManualTests)) } 13 | func TestPlan932Testdata(t *testing.T) { testPlan932(t, concat(basicPrefixes, testdataCases(t))) } 14 | func TestPlan932ModRM(t *testing.T) { testPlan932(t, concat(basicPrefixes, enumModRM)) } 15 | func TestPlan932OneByte(t *testing.T) { testBasic(t, testPlan932) } 16 | func TestPlan9320F(t *testing.T) { testBasic(t, testPlan932, 0x0F) } 17 | func TestPlan9320F38(t *testing.T) { testBasic(t, testPlan932, 0x0F, 0x38) } 18 | func TestPlan9320F3A(t *testing.T) { testBasic(t, testPlan932, 0x0F, 0x3A) } 19 | func TestPlan932Prefix(t *testing.T) { testPrefix(t, testPlan932) } 20 | 21 | func TestPlan964Manual(t *testing.T) { testPlan964(t, hexCases(t, plan9ManualTests)) } 22 | func TestPlan964Testdata(t *testing.T) { testPlan964(t, concat(basicPrefixes, testdataCases(t))) } 23 | func TestPlan964ModRM(t *testing.T) { testPlan964(t, concat(basicPrefixes, enumModRM)) } 24 | func TestPlan964OneByte(t *testing.T) { testBasic(t, testPlan964) } 25 | func TestPlan9640F(t *testing.T) { testBasic(t, testPlan964, 0x0F) } 26 | func TestPlan9640F38(t *testing.T) { testBasic(t, testPlan964, 0x0F, 0x38) } 27 | func TestPlan9640F3A(t *testing.T) { testBasic(t, testPlan964, 0x0F, 0x3A) } 28 | func TestPlan964Prefix(t *testing.T) { testPrefix(t, testPlan964) } 29 | 30 | func TestPlan964REXTestdata(t *testing.T) { 31 | testPlan964(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX)) 32 | } 33 | func TestPlan964REXModRM(t *testing.T) { 34 | testPlan964(t, concat3(basicPrefixes, rexPrefixes, enumModRM)) 35 | } 36 | func TestPlan964REXOneByte(t *testing.T) { testBasicREX(t, testPlan964) } 37 | func TestPlan964REX0F(t *testing.T) { testBasicREX(t, testPlan964, 0x0F) } 38 | func TestPlan964REX0F38(t *testing.T) { testBasicREX(t, testPlan964, 0x0F, 0x38) } 39 | func TestPlan964REX0F3A(t *testing.T) { testBasicREX(t, testPlan964, 0x0F, 0x3A) } 40 | func TestPlan964REXPrefix(t *testing.T) { testPrefixREX(t, testPlan964) } 41 | 42 | // plan9ManualTests holds test cases that will be run by TestPlan9Manual32 and TestPlan9Manual64. 43 | // If you are debugging a few cases that turned up in a longer run, it can be useful 44 | // to list them here and then use -run=Plan9Manual, particularly with tracing enabled. 45 | var plan9ManualTests = ` 46 | ` 47 | 48 | // allowedMismatchPlan9 reports whether the mismatch between text and dec 49 | // should be allowed by the test. 50 | func allowedMismatchPlan9(text string, size int, inst *Inst, dec ExtInst) bool { 51 | return false 52 | } 53 | 54 | // Instructions known to us but not to plan9. 55 | var plan9Unsupported = strings.Fields(` 56 | `) 57 | -------------------------------------------------------------------------------- /x86/x86asm/testdata/Makefile: -------------------------------------------------------------------------------- 1 | libmach8db: libmach8db.c 2 | 9c libmach8db.c && 9l -o libmach8db libmach8db.o; rm libmach8db.o 3 | 4 | newdecode.txt: 5 | cd ..; go test -cover -run 'Objdump.*32' -v -timeout 10h -printtests 2>&1 | tee log 6 | cd ..; go test -cover -run 'Objdump.*64' -v -timeout 10h -printtests 2>&1 | tee -a log 7 | cd ..; go test -cover -run 'Xed.*32' -v -timeout 10h -printtests 2>&1 | tee -a log 8 | cd ..; go test -cover -run 'Xed.*64' -v -timeout 10h -printtests 2>&1 | tee -a log 9 | cd ..; go test -cover -run 'Plan9.*32' -v -timeout 10h -printtests 2>&1 | tee -a log 10 | cd ..; go test -cover -run 'Plan9.*64' -v -timeout 10h -printtests 2>&1 | tee -a log 11 | egrep ' (gnu|intel|plan9) ' ../log |sort >newdecode.txt 12 | 13 | -------------------------------------------------------------------------------- /x86/x86asm/xedext_test.go: -------------------------------------------------------------------------------- 1 | package x86asm 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | // xed binary from Intel sde-external-6.22.0-2014-03-06. 15 | const xedPath = "/Users/rsc/bin/xed" 16 | 17 | func testXedArch(t *testing.T, arch int, generate func(func([]byte))) { 18 | if testing.Short() { 19 | t.Skip("skipping xed test in short mode") 20 | } 21 | if _, err := os.Stat(xedPath); err != nil { 22 | t.Skip(err) 23 | } 24 | 25 | testExtDis(t, "intel", arch, xed, generate, allowedMismatchXed) 26 | } 27 | 28 | func testXed32(t *testing.T, generate func(func([]byte))) { 29 | testXedArch(t, 32, generate) 30 | } 31 | 32 | func testXed64(t *testing.T, generate func(func([]byte))) { 33 | testXedArch(t, 64, generate) 34 | } 35 | 36 | func xed(ext *ExtDis) error { 37 | b, err := ext.Run(xedPath, fmt.Sprintf("-%d", ext.Arch), "-n", "1G", "-ir", ext.File.Name()) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | nmatch := 0 43 | next := uint32(start) 44 | var ( 45 | addr uint32 46 | encbuf [32]byte 47 | enc []byte 48 | text string 49 | ) 50 | 51 | var xedEnd = []byte("# end of text section") 52 | var xedEnd1 = []byte("# Errors") 53 | 54 | eof := false 55 | for { 56 | line, err := b.ReadSlice('\n') 57 | if err != nil { 58 | if err == io.EOF { 59 | break 60 | } 61 | return fmt.Errorf("reading objdump output: %v", err) 62 | } 63 | if debug { 64 | os.Stdout.Write(line) 65 | } 66 | if bytes.HasPrefix(line, xedEnd) || bytes.HasPrefix(line, xedEnd1) { 67 | eof = true 68 | } 69 | if eof { 70 | continue 71 | } 72 | nmatch++ 73 | addr, enc, text = parseLineXed(line, encbuf[:0]) 74 | if addr > next { 75 | return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line) 76 | } 77 | if addr < next { 78 | continue 79 | } 80 | switch text { 81 | case "repz": 82 | text = "rep" 83 | case "repnz": 84 | text = "repn" 85 | default: 86 | text = strings.Replace(text, "repz ", "rep ", -1) 87 | text = strings.Replace(text, "repnz ", "repn ", -1) 88 | } 89 | if m := pcrelw.FindStringSubmatch(text); m != nil { 90 | targ, _ := strconv.ParseUint(m[2], 16, 64) 91 | text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc)))) 92 | } 93 | if m := pcrel.FindStringSubmatch(text); m != nil { 94 | targ, _ := strconv.ParseUint(m[2], 16, 64) 95 | text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc)))) 96 | } 97 | ext.Dec <- ExtInst{addr, encbuf, len(enc), text} 98 | encbuf = [32]byte{} 99 | enc = nil 100 | next += 32 101 | } 102 | if next != start+uint32(ext.Size) { 103 | return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size) 104 | } 105 | if err := ext.Wait(); err != nil { 106 | return fmt.Errorf("exec: %v", err) 107 | } 108 | 109 | return nil 110 | } 111 | 112 | var ( 113 | xedInRaw = []byte("In raw...") 114 | xedDots = []byte("...") 115 | xdis = []byte("XDIS ") 116 | xedError = []byte("ERROR: ") 117 | xedNoDecode = []byte("Could not decode at offset: 0x") 118 | ) 119 | 120 | func parseLineXed(line []byte, encstart []byte) (addr uint32, enc []byte, text string) { 121 | oline := line 122 | if bytes.HasPrefix(line, xedInRaw) || bytes.HasPrefix(line, xedDots) { 123 | return 0, nil, "" 124 | } 125 | if bytes.HasPrefix(line, xedError) { 126 | i := bytes.IndexByte(line[len(xedError):], ' ') 127 | if i < 0 { 128 | log.Fatalf("cannot parse error: %q", oline) 129 | } 130 | errstr := string(line[len(xedError):]) 131 | i = bytes.Index(line, xedNoDecode) 132 | if i < 0 { 133 | log.Fatalf("cannot parse error: %q", oline) 134 | } 135 | i += len(xedNoDecode) 136 | j := bytes.IndexByte(line[i:], ' ') 137 | if j < 0 { 138 | log.Fatalf("cannot parse error: %q", oline) 139 | } 140 | x, err := strconv.ParseUint(string(trimSpace(line[i:i+j])), 16, 32) 141 | if err != nil { 142 | log.Fatalf("cannot parse disassembly: %q", oline) 143 | } 144 | addr = uint32(x) 145 | return addr, nil, errstr 146 | } 147 | 148 | if !bytes.HasPrefix(line, xdis) { 149 | log.Fatalf("cannot parse disassembly: %q", oline) 150 | } 151 | 152 | i := bytes.IndexByte(line, ':') 153 | if i < 0 { 154 | log.Fatalf("cannot parse disassembly: %q", oline) 155 | } 156 | x, err := strconv.ParseUint(string(trimSpace(line[len(xdis):i])), 16, 32) 157 | if err != nil { 158 | log.Fatalf("cannot parse disassembly: %q", oline) 159 | } 160 | addr = uint32(x) 161 | 162 | // spaces 163 | i++ 164 | for i < len(line) && line[i] == ' ' { 165 | i++ 166 | } 167 | // instruction class, spaces 168 | for i < len(line) && line[i] != ' ' { 169 | i++ 170 | } 171 | for i < len(line) && line[i] == ' ' { 172 | i++ 173 | } 174 | // instruction set, spaces 175 | for i < len(line) && line[i] != ' ' { 176 | i++ 177 | } 178 | for i < len(line) && line[i] == ' ' { 179 | i++ 180 | } 181 | 182 | // hex 183 | hexStart := i 184 | for i < len(line) && line[i] != ' ' { 185 | i++ 186 | } 187 | hexEnd := i 188 | for i < len(line) && line[i] == ' ' { 189 | i++ 190 | } 191 | 192 | // text 193 | textStart := i 194 | for i < len(line) && line[i] != '\n' { 195 | i++ 196 | } 197 | textEnd := i 198 | 199 | enc, ok := parseHex(line[hexStart:hexEnd], encstart) 200 | if !ok { 201 | log.Fatalf("cannot parse disassembly: %q", oline) 202 | } 203 | 204 | return addr, enc, string(fixSpace(line[textStart:textEnd])) 205 | } 206 | -------------------------------------------------------------------------------- /x86/x86avxgen/instruction.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "strings" 9 | 10 | "golang.org/x/arch/x86/xeddata" 11 | ) 12 | 13 | // argument is a describes single instruction operand properties. 14 | type argument struct { 15 | // ytype is argument class as returned by asm6 "oclass" function. 16 | ytype string 17 | 18 | // zkind is a partial Z-case matcher. 19 | // Determines which Z-case handles the encoding of instruction. 20 | zkind string 21 | } 22 | 23 | // instruction is decoded XED instruction. 24 | // Used to produce ytabs and optabs in later phases. 25 | type instruction struct { 26 | // opcode is instruction symbolic name. 27 | opcode string 28 | 29 | pset xeddata.PatternSet 30 | enc *encoding 31 | 32 | // mask is EVEX K-register argument; points to args element. 33 | // Used to emit Yk0+Yknot0 table entries. 34 | // Nil for VEX-encoded insts. 35 | mask *argument 36 | args []*argument 37 | 38 | // zform is a pattern that determines which encoder Z-case is used. 39 | // We store zform instead of zcase directly because it's further 40 | // expanded during optabs generation. 41 | zform string 42 | } 43 | 44 | // String returns short inst printed representation. 45 | func (inst *instruction) String() string { return inst.opcode } 46 | 47 | // YtypeListString joins each argument Y-type and returns the result. 48 | func (inst *instruction) YtypeListString() string { 49 | var parts []string 50 | for _, arg := range inst.args { 51 | parts = append(parts, arg.ytype) 52 | } 53 | return strings.Join(parts, " ") 54 | } 55 | 56 | // ArgIndexByZkind returns first argument matching given zkind or -1. 57 | func (inst *instruction) ArgIndexByZkind(zkind string) int { 58 | for i, arg := range inst.args { 59 | if arg.zkind == zkind { 60 | return i 61 | } 62 | } 63 | return -1 64 | } 65 | -------------------------------------------------------------------------------- /x86/x86avxgen/print.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "go/format" 10 | "io" 11 | "log" 12 | "sort" 13 | "text/template" 14 | ) 15 | 16 | var tablesTemplate = template.Must(template.New("avx_optabs").Parse(` 17 | // Code generated by x86avxgen. DO NOT EDIT. 18 | 19 | package x86 20 | 21 | // VEX instructions that come in two forms: 22 | // VTHING xmm2/m128, xmmV, xmm1 23 | // VTHING ymm2/m256, ymmV, ymm1 24 | // 25 | // The opcode array in the corresponding Optab entry 26 | // should contain the (VEX prefixes, opcode byte) pair 27 | // for each of the two forms. 28 | // For example, the entries for VPXOR are: 29 | // 30 | // VPXOR xmm2/m128, xmmV, xmm1 31 | // VEX.NDS.128.66.0F.WIG EF /r 32 | // 33 | // VPXOR ymm2/m256, ymmV, ymm1 34 | // VEX.NDS.256.66.0F.WIG EF /r 35 | // 36 | // Produce this optab entry: 37 | // 38 | // {AVPXOR, yvex_xy3, Pavx, opBytes{vex128|vex66|vex0F|vexWIG, 0xEF, vex256|vex66|vex0F|vexWIG, 0xEF}} 39 | // 40 | // VEX requires at least 2 bytes inside opBytes: 41 | // - VEX prefixes (vex-prefixed constants) 42 | // - Opcode byte 43 | // 44 | // EVEX instructions extend VEX form variety: 45 | // VTHING zmm2/m512, zmmV, zmm1 -- implicit K0 (merging) 46 | // VTHING zmm2/m512, zmmV, K, zmm1 -- explicit K mask (can't use K0) 47 | // 48 | // EVEX requires at least 3 bytes inside opBytes: 49 | // - EVEX prefixes (evex-prefixed constants); similar to VEX 50 | // - Displacement multiplier info (scale / broadcast scale) 51 | // - Opcode byte; similar to VEX 52 | // 53 | // Both VEX and EVEX instructions may have opdigit (opcode extension) byte 54 | // which follows the primary opcode byte. 55 | // Because it can only have value of 0-7, it is written in octal notation. 56 | // 57 | // x86.csv can be very useful for figuring out proper [E]VEX parts. 58 | 59 | {{ range .Ylists }} 60 | var {{.Name}} = []ytab{ 61 | {{- range .Ytabs }} 62 | {zcase: {{.Zcase}}, zoffset: {{.Zoffset}}, args: argList{ {{.ArgList}} }}, 63 | {{- end }} 64 | } 65 | {{ end }} 66 | 67 | var avxOptab = [...]Optab{ 68 | {{- range .Optabs }} 69 | {as: {{.Opcode}}, ytab: {{.YtabList.Name}}, prefix: Pavx, op: opBytes{ 70 | {{- range .OpLines }} 71 | {{.}}, 72 | {{- end }} 73 | }}, 74 | {{- end }} 75 | } 76 | `)) 77 | 78 | // writeTables writes avx optabs file contents to w. 79 | func writeTables(w io.Writer, ctx *context) { 80 | ylists := make([]*ytabList, 0, len(ctx.ytabLists)) 81 | for _, ylist := range ctx.ytabLists { 82 | ylists = append(ylists, ylist) 83 | } 84 | sort.Slice(ylists, func(i, j int) bool { 85 | return ylists[i].Name < ylists[j].Name 86 | }) 87 | optabs := make([]*optab, 0, len(ctx.optabs)) 88 | for _, o := range ctx.optabs { 89 | optabs = append(optabs, o) 90 | } 91 | sort.Slice(optabs, func(i, j int) bool { 92 | return optabs[i].Opcode < optabs[j].Opcode 93 | }) 94 | 95 | var buf bytes.Buffer 96 | err := tablesTemplate.Execute(&buf, struct { 97 | Ylists []*ytabList 98 | Optabs []*optab 99 | }{ 100 | Ylists: ylists, 101 | Optabs: optabs, 102 | }) 103 | if err != nil { 104 | log.Fatalf("template execute error: %v", err) 105 | } 106 | 107 | // TODO: invoke "go fmt" or format.Gofmt? #22695. 108 | prettyCode, err := format.Source(buf.Bytes()) 109 | if err != nil { 110 | log.Fatalf("gofmt error: %v", err) 111 | } 112 | 113 | if _, err := w.Write(prettyCode); err != nil { 114 | log.Fatalf("write output: %v", err) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /x86/x86avxgen/testdata/xedpath/all-element-types.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | ###FILE: ./datafiles/xed-operand-types.txt 4 | 5 | #BEGIN_LEGAL 6 | # 7 | #Copyright (c) 2016 Intel Corporation 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | #END_LEGAL 22 | # 23 | # 24 | #XTYPE TYPE BITS-PER-ELEM 25 | # 26 | var VARIABLE 0 # instruction must set NELEM and ELEMENT_SIZE 27 | struct STRUCT 0 # many elements of different widths 28 | int INT 0 # one element, all the bits, width varies 29 | uint UINT 0 # one element, all the bits, width varies 30 | # 31 | i1 INT 1 32 | i8 INT 8 33 | i16 INT 16 34 | i32 INT 32 35 | i64 INT 64 36 | u8 UINT 8 37 | u16 UINT 16 38 | u32 UINT 32 39 | u64 UINT 64 40 | u128 UINT 128 41 | u256 UINT 256 42 | f32 SINGLE 32 43 | f64 DOUBLE 64 44 | f80 LONGDOUBLE 80 45 | b80 LONGBCD 80 46 | 47 | 48 | ###FILE: ./datafiles/ivbavx/fp16-operand-types.txt 49 | 50 | #BEGIN_LEGAL 51 | # 52 | #Copyright (c) 2016 Intel Corporation 53 | # 54 | # Licensed under the Apache License, Version 2.0 (the "License"); 55 | # you may not use this file except in compliance with the License. 56 | # You may obtain a copy of the License at 57 | # 58 | # http://www.apache.org/licenses/LICENSE-2.0 59 | # 60 | # Unless required by applicable law or agreed to in writing, software 61 | # distributed under the License is distributed on an "AS IS" BASIS, 62 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 63 | # See the License for the specific language governing permissions and 64 | # limitations under the License. 65 | # 66 | #END_LEGAL 67 | #XTYPE TYPE BITS-PER-ELEM 68 | f16 FLOAT16 16 69 | 70 | -------------------------------------------------------------------------------- /x86/x86avxgen/testdata/xedpath/all-extra-widths.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2025 The Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | -------------------------------------------------------------------------------- /x86/x86csv/reader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x86csv 6 | 7 | import ( 8 | "encoding/csv" 9 | "io" 10 | ) 11 | 12 | // A Reader reads entries from an "x86.csv" file. 13 | type Reader struct { 14 | csv *csv.Reader 15 | } 16 | 17 | // NewReader returns a Reader reading from r, which should 18 | // be of the content of the "x86.csv" (format version=0.2). 19 | func NewReader(r io.Reader) *Reader { 20 | rcsv := csv.NewReader(r) 21 | rcsv.Comment = '#' 22 | return &Reader{csv: rcsv} 23 | } 24 | 25 | // ReadAll reads all remaining rows from r. 26 | // 27 | // If error has occurred, still returns all rows 28 | // that have been read during method execution. 29 | // 30 | // A successful call returns err == nil, not err == io.EOF. 31 | // Because ReadAll is defined to read until EOF, 32 | // it does not treat end of file as an error to be reported. 33 | func (r *Reader) ReadAll() ([]*Inst, error) { 34 | var err error 35 | var insts []*Inst 36 | for inst, err := r.Read(); err == nil; inst, err = r.Read() { 37 | insts = append(insts, inst) 38 | } 39 | if err == io.EOF { 40 | return insts, nil 41 | } 42 | return insts, err 43 | } 44 | 45 | // Read reads and returns the next Row from the "x86.csv" file. 46 | // If there is no data left to be read, Read returns {nil, io.EOF}. 47 | func (r *Reader) Read() (*Inst, error) { 48 | cols, err := r.csv.Read() 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | // This should be the only place where indexes 54 | // are used. Everything else should rely on Row records. 55 | inst := &Inst{ 56 | Intel: cols[0], 57 | Go: cols[1], 58 | GNU: cols[2], 59 | Encoding: cols[3], 60 | Mode32: cols[4], 61 | Mode64: cols[5], 62 | CPUID: cols[6], 63 | Tags: cols[7], 64 | Action: cols[8], 65 | Multisize: cols[9], 66 | DataSize: cols[10], 67 | } 68 | return inst, nil 69 | } 70 | -------------------------------------------------------------------------------- /x86/x86csv/x86csv.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package x86csv provides means to work with "x86.csv". 6 | // Only latest version of "x86.csv" format is supported. 7 | // 8 | // Terminology: 9 | // given "OPCODE [ARGS...]" line; 10 | // Opcode - instruction name/mnemonic/class. 11 | // Args - instruction operands. 12 | // Syntax - Opcode with Args. 13 | package x86csv 14 | 15 | import ( 16 | "strings" 17 | ) 18 | 19 | // An Inst describes single x86 instruction encoding form. 20 | type Inst struct { 21 | // Intel syntax (example: "SHR r/m32, imm8"). 22 | Intel string 23 | 24 | // Go assembler syntax (example: "SHRL imm8, r/m32"). 25 | Go string 26 | 27 | // GNU binutils syntax (example: "shrl imm8, r/m32"). 28 | GNU string 29 | 30 | // Binary encoding (example: "C1 /4 ib"). 31 | Encoding string 32 | 33 | // Validity in 32bit mode ("V", "I" or "N.E."). 34 | Mode32 string 35 | 36 | // Validity in 64bit mode ("V", "I", "N.E.", "N.P.", "N.I." or "N.S."). 37 | Mode64 string 38 | 39 | // CPUID feature flags required (comma-separated). 40 | CPUID string 41 | 42 | // Hints about instruction (comma-separated). 43 | // See "x86spec" package to see detailed overview of possible 44 | // tags and their meaning. 45 | Tags string 46 | 47 | // Read/write action of the instruction on its arguments, in Intel order. 48 | // For example, "rw,r" denotes that "SHR r/m32, imm8" reads and writes 49 | // its first argument but only reads its second argument. 50 | Action string 51 | 52 | // Whether Intel syntax has encoding forms distinguished only by 53 | // operand size, like most arithmetic instructions ("" or "Y"). 54 | Multisize string 55 | 56 | // DataSize is the size of the data operation in bits ("8" for MOVB, "16" for MOVW, and so on). 57 | DataSize string 58 | } 59 | 60 | // IntelOpcode returns the opcode in the Intel syntax. 61 | func (inst *Inst) IntelOpcode() string { return instOpcode(inst.Intel) } 62 | 63 | // GoOpcode returns the opcode in Go (Plan9) syntax. 64 | func (inst *Inst) GoOpcode() string { return instOpcode(inst.Go) } 65 | 66 | // GNUOpcode returns the opcode in GNU binutils (mostly AT&T) syntax. 67 | func (inst *Inst) GNUOpcode() string { return instOpcode(inst.GNU) } 68 | 69 | // IntelArgs returns the arguments in the Intel syntax. 70 | func (inst *Inst) IntelArgs() []string { return instArgs(inst.Intel) } 71 | 72 | // GoArgs returns the arguments in Go (Plan9) syntax. 73 | func (inst *Inst) GoArgs() []string { return instArgs(inst.Go) } 74 | 75 | // GNUArgs returns the arguments in GNU binutils (mostly AT&T) syntax. 76 | func (inst *Inst) GNUArgs() []string { return instArgs(inst.GNU) } 77 | 78 | // HasTag reports whether inst tag list contains the specified tag. 79 | func (inst *Inst) HasTag(tag string) bool { 80 | i := strings.Index(inst.Tags, tag) 81 | if i == -1 { 82 | return false 83 | } 84 | leftOK := i == 0 || 85 | (inst.Tags[i-1] == ',') 86 | rigthOK := i+len(tag) == len(inst.Tags) || 87 | (inst.Tags[i+len(tag)] == ',') 88 | return leftOK && rigthOK 89 | } 90 | 91 | // instOpcode returns the opcode from an instruction syntax. 92 | func instOpcode(syntax string) string { 93 | i := strings.Index(syntax, " ") 94 | if i == -1 { 95 | return syntax 96 | } 97 | return syntax[:i] 98 | } 99 | 100 | // instArgs returns the arguments from an instruction syntax. 101 | func instArgs(syntax string) []string { 102 | i := strings.Index(syntax, " ") 103 | if i < 0 { 104 | return nil 105 | } 106 | args := strings.Split(syntax[i+1:], ",") 107 | for i := range args { 108 | args[i] = strings.TrimSpace(args[i]) 109 | } 110 | return args 111 | } 112 | -------------------------------------------------------------------------------- /x86/x86csv/x86csv_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package x86csv 6 | 7 | import ( 8 | "reflect" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | // This test makes it harder to break Reader unintentionally. 14 | // 15 | // Deeper testing is probably not required because 99% of the job is 16 | // done by csv.Reader. 17 | func TestReader(t *testing.T) { 18 | input := `# x86.csv v0.2 19 | "ADDSUBPD xmm1, xmm2/m128","ADDSUBPD xmm2/m128, xmm1","addsubpd xmm2/m128, xmm1","66 0F D0 /r","V","V","SSE3","","rw,r","","" 20 | "VPEXTRQ r/m64, xmm1, imm8","VPEXTRQ imm8, xmm1, r/m64","vpextrq imm8, xmm1, r/m64","VEX.128.66.0F3A.W1 16 /r ib","I","V","AVX","","w,r,r","","" 21 | "XOR r8, r/m8","XORB r/m8, r8","xorb r/m8, r8","REX 32 /r","N.E.","V","","pseudo64","rw,r","Y","8" 22 | ` 23 | want := []Inst{ 24 | { 25 | Intel: "ADDSUBPD xmm1, xmm2/m128", 26 | Go: "ADDSUBPD xmm2/m128, xmm1", 27 | GNU: "addsubpd xmm2/m128, xmm1", 28 | Encoding: "66 0F D0 /r", 29 | Mode32: "V", 30 | Mode64: "V", 31 | CPUID: "SSE3", 32 | Action: "rw,r", 33 | }, 34 | { 35 | Intel: "VPEXTRQ r/m64, xmm1, imm8", 36 | Go: "VPEXTRQ imm8, xmm1, r/m64", 37 | GNU: "vpextrq imm8, xmm1, r/m64", 38 | Encoding: "VEX.128.66.0F3A.W1 16 /r ib", 39 | Mode32: "I", 40 | Mode64: "V", 41 | CPUID: "AVX", 42 | Action: "w,r,r", 43 | }, 44 | { 45 | Intel: "XOR r8, r/m8", 46 | Go: "XORB r/m8, r8", 47 | GNU: "xorb r/m8, r8", 48 | Encoding: "REX 32 /r", 49 | Mode32: "N.E.", 50 | Mode64: "V", 51 | Tags: "pseudo64", 52 | Action: "rw,r", 53 | Multisize: "Y", 54 | DataSize: "8", 55 | }, 56 | } 57 | 58 | r := NewReader(strings.NewReader(input)) 59 | inst, err := r.Read() 60 | if err != nil { 61 | t.Fatalf("Read(): %v", err) 62 | } 63 | restInsts, err := r.ReadAll() 64 | if err != nil { 65 | t.Fatalf("ReadAll(): %v", err) 66 | } 67 | if remainder, err := r.ReadAll(); remainder != nil || err != nil { 68 | t.Errorf("ReadAll() on exhausted r failed") 69 | } 70 | have := append([]*Inst{inst}, restInsts...) 71 | 72 | if len(want) != len(have) { 73 | t.Fatalf("len(have) is %d, want %d\n", len(have), len(want)) 74 | } 75 | lines := strings.Split(input, "\n") 76 | lines = lines[1:] // Drop comment line 77 | for i := range want { 78 | if want[i] != *have[i] { 79 | t.Errorf("%s:\nhave: %v\nwant: %v", lines[i], have[i], want[i]) 80 | } 81 | } 82 | } 83 | 84 | func TestSyntaxSplit(t *testing.T) { 85 | tests := []struct { 86 | syntax string 87 | opcode string 88 | args []string 89 | }{ 90 | {"RET", "RET", nil}, 91 | {"CALLW* r/m16", "CALLW*", []string{"r/m16"}}, 92 | {"JMP_FAR m16:16", "JMP_FAR", []string{"m16:16"}}, 93 | {"movl CR0-CR7, rmr32", "movl", []string{"CR0-CR7", "rmr32"}}, 94 | {"VFMSUBADD132PD xmm1, xmmV, xmm2/m128", "VFMSUBADD132PD", []string{"xmm1", "xmmV", "xmm2/m128"}}, 95 | } 96 | 97 | for _, tt := range tests { 98 | op, args := instOpcode(tt.syntax), instArgs(tt.syntax) 99 | if op != tt.opcode { 100 | t.Errorf("%s: opcode mismatch (have `%s`, want `%s`)", 101 | tt.syntax, op, tt.opcode) 102 | } 103 | if !reflect.DeepEqual(args, tt.args) { 104 | t.Errorf("%s: args mismatch (have %v, want %s)", 105 | tt.syntax, args, tt.args) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /x86/x86spec/.gitignore: -------------------------------------------------------------------------------- 1 | x86manual.pdf 2 | -------------------------------------------------------------------------------- /x86/xeddata/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package xeddata provides utilities to work with XED datafiles. 6 | // 7 | // Main features: 8 | // - Fundamental XED enumerations (CPU modes, operand sizes, ...) 9 | // - XED objects and their components 10 | // - XED datafiles reader (see below) 11 | // - Utility functions like ExpandStates 12 | // 13 | // The amount of file formats that is understood is a minimal 14 | // set required to generate x86.csv from XED tables: 15 | // - states - simple macro substitutions used in patterns 16 | // - widths - mappings from width names to their size 17 | // - element-types - XED xtype information 18 | // - objects - XED objects that constitute "the tables" 19 | // 20 | // Collectively, those files are called "datafiles". 21 | // 22 | // Terminology is borrowed from XED itself, 23 | // where appropriate, x86csv names are provided 24 | // as an alternative. 25 | // 26 | // Suppose $XED is the path of a checkout of the 27 | // https://github.com/intelxed/xed repo. 28 | // 29 | // "$XED/foo/bar.txt" notation is used to specify a path to "foo/bar.txt" 30 | // file under local XED source repository folder. 31 | // 32 | // The default usage scheme: 33 | // 1. Open "XED database" to load required metadata. 34 | // 2. Read XED file with objects definitions. 35 | // 3. Operate on XED objects. 36 | // 37 | // See example_test.go for complete examples. 38 | // See testdata/xed_objects.txt for examples of "XED objects". 39 | // 40 | // # Obtain XED datafiles 41 | // 42 | // It is required to build Intel XED before attempting to use 43 | // its datafiles, as this package expects the "all" versions that 44 | // are a concatenated final versions of datafiles. 45 | // To build it, follow the instruction on https://github.com/intelxed/xed. 46 | // 47 | // Once built, the "all" versions of data files are in "$XED/obj/dgen/". 48 | // If "$XED/obj/dgen/" does not contain relevant files, 49 | // then either this documentation is stale or your XED is not built. 50 | // Pass $XED/obj/dgen (or a copy of it) as the "xedPath" to [NewDatabase] 51 | // or to x86avxgen -xedPath. 52 | // 53 | // Intel XED https://github.com/intelxed/xed provides all documentation 54 | // that can be required to understand datafiles. 55 | // The "$XED/misc/engineering-notes.txt" is particularly useful. 56 | // For convenience, the most important notes are spread across package comments. 57 | // 58 | // Tested with XED 088c48a2efa447872945168272bcd7005a7ddd91. 59 | package xeddata 60 | -------------------------------------------------------------------------------- /x86/xeddata/pattern_set.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xeddata 6 | 7 | import ( 8 | "sort" 9 | "strings" 10 | ) 11 | 12 | // PatternSet wraps instruction PATTERN properties providing set operations on them. 13 | type PatternSet map[string]bool 14 | 15 | // NewPatternSet decodes pattern string into PatternSet. 16 | func NewPatternSet(pattern string) PatternSet { 17 | pset := make(PatternSet) 18 | for _, f := range strings.Fields(pattern) { 19 | pset[f] = true 20 | } 21 | return pset 22 | } 23 | 24 | // PatternAliases is extendable map of pattern keys aliases. 25 | // Maps human-readable key to XED property. 26 | // 27 | // Used in PatternSet.Is. 28 | var PatternAliases = map[string]string{ 29 | "VEX": "VEXVALID=1", 30 | "EVEX": "VEXVALID=2", 31 | "XOP": "VEXVALID=3", 32 | "MemOnly": "MOD!=3", 33 | "RegOnly": "MOD=3", 34 | } 35 | 36 | // String returns pattern printer representation. 37 | // All properties are sorted. 38 | func (pset PatternSet) String() string { 39 | var keys []string 40 | for k := range pset { 41 | keys = append(keys, k) 42 | } 43 | sort.Strings(keys) 44 | return strings.Join(keys, " ") 45 | } 46 | 47 | // Is reports whether set contains key k. 48 | // In contrast with direct pattern set lookup, it does 49 | // check if PatternAliases[k] is available to be used instead of k in lookup. 50 | func (pset PatternSet) Is(k string) bool { 51 | if alias := PatternAliases[k]; alias != "" { 52 | return pset[alias] 53 | } 54 | return pset[k] 55 | } 56 | 57 | // Replace inserts newKey if oldKey is defined. 58 | // oldKey is removed if insertion is performed. 59 | func (pset PatternSet) Replace(oldKey, newKey string) { 60 | if pset[oldKey] { 61 | pset[newKey] = true 62 | delete(pset, oldKey) 63 | } 64 | } 65 | 66 | // Index returns index from keys of first matching key. 67 | // Returns -1 if does not contain any of given keys. 68 | func (pset PatternSet) Index(keys ...string) int { 69 | for i, k := range keys { 70 | if pset[k] { 71 | return i 72 | } 73 | } 74 | return -1 75 | } 76 | 77 | // Match is like MatchOrDefault("", keyval...). 78 | func (pset PatternSet) Match(keyval ...string) string { 79 | return pset.MatchOrDefault("", keyval...) 80 | } 81 | 82 | // MatchOrDefault returns first matching key associated value. 83 | // Returns defaultValue if no match is found. 84 | // 85 | // Keyval structure can be described as {"k1", "v1", ..., "kN", "vN"}. 86 | func (pset PatternSet) MatchOrDefault(defaultValue string, keyval ...string) string { 87 | for i := 0; i < len(keyval); i += 2 { 88 | key := keyval[i+0] 89 | val := keyval[i+1] 90 | if pset[key] { 91 | return val 92 | } 93 | } 94 | return defaultValue 95 | } 96 | -------------------------------------------------------------------------------- /x86/xeddata/reader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xeddata 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "io" 11 | "iter" 12 | "regexp" 13 | "strings" 14 | ) 15 | 16 | // Reader reads enc/dec-instruction objects from XED datafile. 17 | type Reader struct { 18 | r io.Reader 19 | 20 | // Initialized on first call to Read 21 | next func() (*Object, error, bool) 22 | stop func() 23 | err error 24 | } 25 | 26 | // NewReader returns a new Reader that reads from r. 27 | func NewReader(r io.Reader) *Reader { 28 | return &Reader{r: r} 29 | } 30 | 31 | // Read reads single XED instruction object from 32 | // the stream backed by reader. 33 | // 34 | // If there is no data left to be read, 35 | // returned error is io.EOF. 36 | func (r *Reader) Read() (*Object, error) { 37 | if r.err != nil { 38 | return nil, r.err 39 | } 40 | if r.next == nil { 41 | r.next, r.stop = iter.Pull2(readObjects(r.r)) 42 | } 43 | obj, err, end := r.next() 44 | if end { 45 | err = io.EOF 46 | } 47 | if err != nil { 48 | r.stop() 49 | r.err, r.next, r.stop = err, nil, nil 50 | return nil, err 51 | } 52 | return obj, nil 53 | } 54 | 55 | // ReadAll reads all the remaining objects from r. 56 | // A successful call returns err == nil, not err == io.EOF, 57 | // just like csv.Reader.ReadAll(). 58 | func (r *Reader) ReadAll() ([]*Object, error) { 59 | var objects []*Object 60 | for obj, err := range readObjects(r.r) { 61 | if err != nil { 62 | return objects, err 63 | } 64 | objects = append(objects, obj) 65 | } 66 | return objects, nil 67 | } 68 | 69 | // readObjects yields all of the objects from r. 70 | func readObjects(r io.Reader) iter.Seq2[*Object, error] { 71 | iterLines := readLines(r) 72 | return func(yield func(*Object, error) bool) { 73 | var blockPos Pos 74 | var block []string // Reused on each iteration 75 | var linePos []Pos 76 | inBlock := false 77 | for line, err := range iterLines { 78 | if err != nil { 79 | yield(nil, err) 80 | return 81 | } 82 | if !inBlock { 83 | inBlock = line.data[0] == '{' 84 | blockPos = line.Pos 85 | } else if line.data[0] == '}' { 86 | inBlock = false 87 | obj, err := parseObjectLines(blockPos, block, linePos) 88 | if !yield(obj, err) { 89 | return 90 | } 91 | block, linePos = block[:0], linePos[:0] 92 | } else { 93 | block = append(block, string(line.data)) 94 | linePos = append(linePos, line.Pos) 95 | } 96 | } 97 | if inBlock { 98 | yield(nil, errors.New("no matching '}' found")) 99 | } 100 | } 101 | } 102 | 103 | // instLineRE matches valid XED object/inst line. 104 | // It expects lines that are joined by '\' to be concatenated. 105 | // 106 | // The format can be described as: 107 | // 108 | // unquoted field name "[A-Z_]+" (captured) 109 | // field value delimiter ":" 110 | // field value string (captured) 111 | var instLineRE = regexp.MustCompile(`^([A-Z_]+)\s*:\s*(.*)`) 112 | 113 | // parseLines turns collected object lines into Object. 114 | func parseObjectLines(blockPos Pos, lines []string, linePos []Pos) (*Object, error) { 115 | o := &Object{} 116 | o.Pos = blockPos 117 | 118 | // Repeatable tokens. 119 | // We can not assign them eagerly, because these fields 120 | // are not guaranteed to follow strict order. 121 | var ( 122 | operands []string 123 | iforms []string 124 | patterns []string 125 | poses []Pos 126 | ) 127 | 128 | for i, l := range lines { 129 | l = strings.TrimLeft(l, " ") 130 | if l[0] == '#' { // Skip comment lines. 131 | continue 132 | } 133 | m := instLineRE.FindStringSubmatch(l) 134 | if len(m) == 0 { 135 | return nil, fmt.Errorf("malformed line: %s", l) 136 | } 137 | key, val := m[1], m[2] 138 | val = strings.TrimSpace(val) 139 | 140 | switch key { 141 | case "ICLASS": 142 | o.Iclass = val 143 | case "DISASM": 144 | o.Disasm = val 145 | case "DISASM_INTEL": 146 | o.DisasmIntel = val 147 | case "DISASM_ATTSV": 148 | o.DisasmATTSV = val 149 | case "ATTRIBUTES": 150 | o.Attributes = val 151 | case "UNAME": 152 | o.Uname = val 153 | case "CPL": 154 | o.CPL = val 155 | case "CATEGORY": 156 | o.Category = val 157 | case "EXTENSION": 158 | o.Extension = val 159 | case "EXCEPTIONS": 160 | o.Exceptions = val 161 | case "ISA_SET": 162 | o.ISASet = val 163 | case "FLAGS": 164 | o.Flags = val 165 | case "COMMENT": 166 | o.Comment = val 167 | case "VERSION": 168 | o.Version = val 169 | case "REAL_OPCODE": 170 | o.RealOpcode = val 171 | 172 | case "OPERANDS": 173 | operands = append(operands, val) 174 | case "PATTERN": 175 | patterns = append(patterns, val) 176 | poses = append(poses, linePos[i]) 177 | case "IFORM": 178 | iforms = append(iforms, val) 179 | 180 | default: 181 | // Being strict about unknown field names gives a nice 182 | // XED file validation diagnostics. 183 | // Also defends against typos in test files. 184 | return nil, fmt.Errorf("unknown key token: %s", key) 185 | } 186 | } 187 | 188 | if len(operands) != len(patterns) { 189 | return nil, fmt.Errorf("%s: OPERANDS and PATTERN lines mismatch", o.Opcode()) 190 | } 191 | 192 | insts := make([]*Inst, len(operands)) 193 | for i := range operands { 194 | insts[i] = &Inst{ 195 | Object: o, 196 | Index: i, 197 | Pattern: patterns[i], 198 | Pos: poses[i], 199 | Operands: operands[i], 200 | } 201 | // There can be less IFORMs than insts. 202 | if i < len(iforms) { 203 | insts[i].Iform = iforms[i] 204 | } 205 | } 206 | o.Insts = insts 207 | 208 | return o, nil 209 | } 210 | -------------------------------------------------------------------------------- /x86/xeddata/readlines.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xeddata 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "fmt" 11 | "io" 12 | "iter" 13 | "path/filepath" 14 | "strings" 15 | ) 16 | 17 | type lineInfo struct { 18 | Pos 19 | data []byte 20 | } 21 | 22 | type Pos struct { 23 | Path string 24 | Line int 25 | } 26 | 27 | func (p Pos) String() string { 28 | if p.Line == 0 { 29 | if p.Path == "" { 30 | return "?:?" 31 | } 32 | return p.Path 33 | } else if p.Path == "" { 34 | return fmt.Sprintf("?:%d", p.Line) 35 | } 36 | return fmt.Sprintf("%s:%d", p.Path, p.Line) 37 | } 38 | 39 | func (p Pos) ShortString() string { 40 | p2 := p 41 | p2.Path = filepath.Base(p.Path) 42 | return p2.String() 43 | } 44 | 45 | // readLines yields lines from r, with continuation lines folded, comments and 46 | // trailing whitespace removed, and blank lines omitted. 47 | // 48 | // The returned lineInfo.data buffer may be reused between yields. 49 | // 50 | // If r has a Name() string method, this is used to populate lineInfo.Path. 51 | func readLines(r io.Reader) iter.Seq2[lineInfo, error] { 52 | type Named interface { 53 | Name() string // Matches os.File 54 | } 55 | path := "" 56 | if f, ok := r.(Named); ok { 57 | path = f.Name() 58 | } 59 | 60 | s := bufio.NewScanner(r) 61 | return func(yield func(lineInfo, error) bool) { 62 | var info lineInfo 63 | info.Path = path 64 | var lineBuf []byte 65 | for s.Scan() { 66 | info.Line++ 67 | 68 | lineBuf = append(lineBuf, s.Bytes()...) 69 | if len(lineBuf) > 0 && lineBuf[len(lineBuf)-1] == '\\' { 70 | // Continuation line. Drop the \ and keep reading. 71 | lineBuf = lineBuf[:len(lineBuf)-1] 72 | continue 73 | } 74 | // Remove comments and trailing whitespace 75 | if i := strings.IndexByte(string(lineBuf), '#'); i >= 0 { 76 | lineBuf = lineBuf[:i] 77 | } 78 | lineBuf = bytes.TrimRight(lineBuf, " \t") 79 | // Don't yield blank lines 80 | if len(lineBuf) == 0 { 81 | continue 82 | } 83 | 84 | info.data = lineBuf 85 | if !yield(info, nil) { 86 | return 87 | } 88 | lineBuf = lineBuf[:0] 89 | } 90 | 91 | if err := s.Err(); err != nil { 92 | yield(lineInfo{}, err) 93 | return 94 | } 95 | if len(lineBuf) > 0 { 96 | yield(lineInfo{}, fmt.Errorf("continuation line at EOF")) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /x86/xeddata/testdata/xedpath/all-element-types.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | fword64 T_DOUBLE 64 6 | -------------------------------------------------------------------------------- /x86/xeddata/testdata/xedpath/all-extra-widths.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2025 The Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | -------------------------------------------------------------------------------- /x86/xeddata/testdata/xedpath/all-state.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | _M_VV_TRUE VEXVALID=1 6 | _M_VV_FALSE VEXVALID=0 7 | 8 | _M_VEX_P_66 VEX_PREFIX=1 9 | _M_VEX_P_F2 VEX_PREFIX=2 10 | _M_VEX_P_F3 VEX_PREFIX=3 11 | 12 | _M_VLEN_128 VL=0 13 | _M_VLEN_256 VL=1 14 | 15 | _M_MAP_0F MAP=1 16 | _M_MAP_0F38 MAP=2 17 | _M_MAP_0F3A MAP=3 18 | -------------------------------------------------------------------------------- /x86/xeddata/testdata/xedpath/all-widths.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | width_dq i32 16 6 | width_qq i32 32 7 | width_v int 2 4 8 8 | width_f64 f64 8 9 | -------------------------------------------------------------------------------- /x86/xeddata/xeddata.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xeddata 6 | 7 | import ( 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | // WalkInsts calls visit function for each XED instruction found at $xedPath/all-dec-instructions.txt. 13 | func WalkInsts(xedPath string, visit func(*Inst)) error { 14 | f, err := os.Open(filepath.Join(xedPath, "all-dec-instructions.txt")) 15 | if err != nil { 16 | return err 17 | } 18 | for obj, err := range readObjects(f) { 19 | if err != nil { 20 | return err 21 | } 22 | for _, inst := range obj.Insts { 23 | visit(inst) 24 | } 25 | } 26 | return nil 27 | } 28 | --------------------------------------------------------------------------------