| // 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 |
| // +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) |
| } |