File: util.go

package info (click to toggle)
golang-golang-x-arch 0.13.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,932 kB
  • sloc: ansic: 1,975; makefile: 59
file content (149 lines) | stat: -rw-r--r-- 4,013 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright 2021 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore

// Generate interesting test cases from ppc64 objdump via
// go run util.go
//
// This requires powerpc64le-linux-gnu-gcc and powerpc64le-linux-gnu-objdump be in
// the PATH this command is run.
//
// These tools can be acquired from the IBM advance toolchain for amd64 hosts too.

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

// Generator for branch on spr (bclr, bctar, bcctr)
func emitBSpr(bo, bi, l uint32, out io.Writer) {
	var insn [3]uint32 = [3]uint32{19<<26 | 16<<1, 19<<26 | 528<<1, 19<<26 | 560<<1}
	for bh := uint32(0); bh < 3; bh++ {
		for _, m := range insn {
			m |= bo << 21
			m |= bi << 16
			m |= bh << 11
			m |= l << 0
			fmt.Fprintf(out, "\t.long 0x%08x\n", m)
		}
	}
}

// Generator for bc
func emitBc(bo, bi, l uint32, out io.Writer) {
	for aa := uint32(0); aa < 2; aa++ {
		m := uint32(16 << 26)
		m |= bo << 21
		m |= bi << 16
		m |= l << 0
		m |= aa << 1
		m |= 128
		fmt.Fprintf(out, "\t.long 0x%08x\n", m)
	}
}

// Generator all interesting conditional branch type instructions
func emitBranches(out io.Writer) {
	fmt.Fprintf(out, ".text\n")
	for bo := 0; bo < 0x20; bo++ {
		// objdump behaves strangely on some cases when a z bit is set.
		// Ignore these, they should never show up in correct code.
		if bo&0x15 == 0x1 {
			// skip 0b0.0.z cases where z != 0
			continue
		}
		if bo&0x14 == 0x14 && bo != 14 {
			// skip 0b1z1zz cases where z != 0
			continue
		}
		// skip at == 1 cases.  objdump doesn't handle these well either.
		reserved_at := map[int]bool{5: true, 13: true, 17: true, 19: true}
		if reserved_at[bo] {
			continue
		}
		// only test cr0/cr1 bits. cr2-cr7 cases are basically identical to cr1.
		for bi := 0; bi < 0x8; bi++ {
			for l := 0; l < 2; l++ {
				emitBSpr(uint32(bo), uint32(bi), uint32(l), out)
				emitBc(uint32(bo), uint32(bi), uint32(l), out)
			}
		}
	}
}

// Emit a test file using the generator called name.txt.  This requires
// a GCC toolchain which supports -mcpu=power10.
func genOutput(name, tcPfx string, generator func(io.Writer)) {
	// Generate object code from gcc
	cmd := exec.Command(tcPfx+"gcc", "-c", "-mbig", "-mcpu=power10", "-x", "assembler-with-cpp", "-o", name+".o", "-")
	input, _ := cmd.StdinPipe()
	cmd.Stderr = os.Stderr
	go func() {
		defer input.Close()
		generator(input.(io.Writer))
	}()
	if cmd.Run() != nil {
		fmt.Printf("Failed running gcc for: %s\n", name)
		return
	}
	defer os.Remove(name + ".o")
	cmd = exec.Command(tcPfx+"objdump", "-d", name+".o")

	// Run objdump and parse output into test format
	output, _ := cmd.StdoutPipe()
	defer output.Close()
	scanner := bufio.NewScanner(output)
	spacere := regexp.MustCompile("[[:space:]]+")
	outf, _ := os.Create(name + ".txt")
	defer outf.Close()
	if cmd.Start() != nil {
		fmt.Printf("Failed running objdump for: %s\n", name)
		return
	}

	pfx := ""
	dec := ""
	for scanner.Scan() {
		ln := spacere.Split(scanner.Text(), -1)
		if len(ln) >= 7 {
			opc := strings.Join(ln[2:6], "")
			if len(pfx) == 0 {
				dec = strings.Join(ln[6:], " ")
			}
			if v, _ := strconv.ParseInt(ln[2], 16, 16); v&0xFC == 0x04 {
				pfx = opc
				continue
			}
			fmt.Fprintf(outf, "%s%s|\tgnu\t%s\n", pfx, opc, dec)
			pfx = ""
		}

	}
	cmd.Wait()
}

// Generate representative instructions for all[1] instructions in pp64.csv.
//
// [1] See hack.h for a few minor, exceptional workarounds.
func emitGenerated(out io.Writer) {
	cmd := exec.Command("go", "run", "../ppc64map/map.go", "-fmt=asm", "../pp64.csv")
	cmdout, _ := cmd.Output()
	out.Write(cmdout)
}

// Produce generated test outputs.  This should be run every so often with
// new versions of objdump to ensure we stay up to date.
func main() {
	genOutput("decode_branch", "powerpc64le-linux-gnu-", emitBranches)
	genOutput("decode_generated", "powerpc64le-linux-gnu-", emitGenerated)
}