// Copyright 2014 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

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var _ = os.Stdout
var _ = fmt.Sprintf

type Inst struct {
	Name   string
	ID     string
	Bits   string
	Arch   string
	Syntax []string
	Code   string
	Base   uint32
	Mask   uint32
	Prog   []*Stmt
}

func main() {
	data, err := ioutil.ReadFile("spec.json")
	if err != nil {
		log.Fatal(err)
	}
	var insts []Inst
	if err := json.Unmarshal(data, &insts); err != nil {
		log.Fatal(err)
	}

	var out []Inst
	for _, inst := range insts {
		inst.Prog = parse(inst.Name+" "+inst.ID, inst.Code)
		if inst.ID[0] == 'A' && !strings.HasPrefix(inst.Syntax[0], "MSR<c>") && !strings.Contains(inst.Syntax[0], "<coproc>") && !strings.Contains(inst.Syntax[0], "VLDM") && !strings.Contains(inst.Syntax[0], "VSTM") {
			out = append(out, inst)
		}
	}
	insts = out

	for i := range insts {
		dosize(&insts[i])
	}

	var cond, special []Inst
	for _, inst := range insts {
		if inst.Base>>28 == 0xF {
			special = append(special, inst)
		} else {
			cond = append(cond, inst)
		}
	}

	fmt.Printf("special:\n")
	split(special, 0xF0000000, 1)
	fmt.Printf("cond:\n")
	split(cond, 0xF0000000, 1)
}

func dosize(inst *Inst) {
	var base, mask uint32
	off := 0
	for _, f := range strings.Split(inst.Bits, "|") {
		if i := strings.Index(f, ":"); i >= 0 {
			n, _ := strconv.Atoi(f[i+1:])
			off += n
			continue
		}
		for _, bit := range strings.Fields(f) {
			switch bit {
			case "0", "(0)":
				mask |= 1 << uint(31-off)
			case "1", "(1)":
				base |= 1 << uint(31-off)
			}
			off++
		}
	}
	if off != 16 && off != 32 {
		log.Printf("incorrect bit count for %s %s: have %d", inst.Name, inst.Bits, off)
	}
	if off == 16 {
		mask >>= 16
		base >>= 16
	}
	mask |= base
	inst.Mask = mask
	inst.Base = base
}

func split(insts []Inst, used uint32, depth int) {
Again:
	if len(insts) <= 1 {
		for _, inst := range insts {
			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))
		}
		return
	}

	m := ^used
	for _, inst := range insts {
		m &= inst.Mask
	}
	if m == 0 {
		fmt.Printf("«%*s%#08x masked out (%d)\n", depth*2, "", used, len(insts))
		for _, inst := range insts {
			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))
		}
		updated := false
		for i := range insts {
			if updateMask(&insts[i]) {
				updated = true
			}
		}
		fmt.Printf("»\n")
		if updated {
			goto Again
		}
		fmt.Printf("%*s%#08x masked out (%d)\n", depth*2, "", used, len(insts))
		for _, inst := range insts {
			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))
		}
		//checkOverlap(used, insts)
		return
	}
	for i := 31; i >= 0; i-- {
		if m&(1<<uint(i)) != 0 {
			m = 1 << uint(i)
			break
		}
	}
	var bit [2][]Inst
	for _, inst := range insts {
		b := (inst.Base / m) & 1
		bit[b] = append(bit[b], inst)
	}

	for b, list := range bit {
		if len(list) > 0 {
			suffix := ""
			if len(bit[1-b]) == 0 {
				suffix = " (only)"
			}
			fmt.Printf("%*sbit %#08x = %d%s\n", depth*2, "", m, b, suffix)
			split(list, used|m, depth+1)
		}
	}
}

var seeRE = regexp.MustCompile(`SEE ([^;\n]+)`)

func updateMask(inst *Inst) bool {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("PANIC:", err)
			return
		}
	}()

	print(".")
	println(inst.Name, inst.ID, inst.Bits)
	println(inst.Code)
	wiggle := ^inst.Mask &^ 0xF0000000
	n := countbits(wiggle)
	m1 := ^uint32(0)
	m2 := ^uint32(0)
	for i := uint32(0); i < 1<<uint(n); i++ {
		w := inst.Base | expand(i, wiggle)
		if !isValid(inst, w) {
			continue
		}
		m1 &= w
		m2 &= ^w
	}
	m := m1 | m2
	m &^= 0xF0000000
	m |= 0xF0000000 & inst.Mask
	if m&^inst.Mask != 0 {
		fmt.Printf("%s %s: mask=%#x but decided %#x\n", inst.Name, inst.ID, inst.Mask, m)
		inst.Mask = m
		inst.Base = m1
		return true
	}
	if inst.Mask&^m != 0 {
		fmt.Printf("%s %s: mask=%#x but got %#x\n", inst.Name, inst.ID, inst.Mask, m)
		panic("bad updateMask")
	}
	return false
}

func countbits(x uint32) int {
	n := 0
	for ; x != 0; x >>= 1 {
		n += int(x & 1)
	}
	return n
}

func expand(x, m uint32) uint32 {
	var out uint32
	for i := uint(0); i < 32; i++ {
		out >>= 1
		if m&1 != 0 {
			out |= (x & 1) << 31
			x >>= 1
		}
		m >>= 1
	}
	return out
}
