// Copyright 2018 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.

package main

import (
	"bytes"
	"go/format"
	"io"
	"log"
	"sort"
	"text/template"
)

var tablesTemplate = template.Must(template.New("avx_optabs").Parse(`
// Code generated by x86avxgen. DO NOT EDIT.

package x86

// VEX instructions that come in two forms:
//	VTHING xmm2/m128, xmmV, xmm1
//	VTHING ymm2/m256, ymmV, ymm1
//
// The opcode array in the corresponding Optab entry
// should contain the (VEX prefixes, opcode byte) pair
// for each of the two forms.
// For example, the entries for VPXOR are:
//
//	VPXOR xmm2/m128, xmmV, xmm1
//	VEX.NDS.128.66.0F.WIG EF /r
//
//	VPXOR ymm2/m256, ymmV, ymm1
//	VEX.NDS.256.66.0F.WIG EF /r
//
// Produce this optab entry:
//
//	{AVPXOR, yvex_xy3, Pavx, opBytes{vex128|vex66|vex0F|vexWIG, 0xEF, vex256|vex66|vex0F|vexWIG, 0xEF}}
//
// VEX requires at least 2 bytes inside opBytes:
//	- VEX prefixes (vex-prefixed constants)
//	- Opcode byte
//
// EVEX instructions extend VEX form variety:
//	VTHING zmm2/m512, zmmV, zmm1    -- implicit K0 (merging)
//	VTHING zmm2/m512, zmmV, K, zmm1 -- explicit K mask (can't use K0)
//
// EVEX requires at least 3 bytes inside opBytes:
//	- EVEX prefixes (evex-prefixed constants); similar to VEX
//	- Displacement multiplier info (scale / broadcast scale)
//	- Opcode byte; similar to VEX
//
// Both VEX and EVEX instructions may have opdigit (opcode extension) byte
// which follows the primary opcode byte.
// Because it can only have value of 0-7, it is written in octal notation.
//
// x86.csv can be very useful for figuring out proper [E]VEX parts.

{{ range .Ylists }}
  var {{.Name}} = []ytab{
    {{- range .Ytabs }}
      {zcase: {{.Zcase}}, zoffset: {{.Zoffset}}, args: argList{ {{.ArgList}} }},
    {{- end }}
  }
{{ end }}

var avxOptab = [...]Optab{
  {{- range .Optabs }}
    {as: {{.Opcode}}, ytab: {{.YtabList.Name}}, prefix: Pavx, op: opBytes{
      {{- range .OpLines }}
        {{.}},
      {{- end }}
    }},
  {{- end }}
}
`))

// writeTables writes avx optabs file contents to w.
func writeTables(w io.Writer, ctx *context) {
	ylists := make([]*ytabList, 0, len(ctx.ytabLists))
	for _, ylist := range ctx.ytabLists {
		ylists = append(ylists, ylist)
	}
	sort.Slice(ylists, func(i, j int) bool {
		return ylists[i].Name < ylists[j].Name
	})
	optabs := make([]*optab, 0, len(ctx.optabs))
	for _, o := range ctx.optabs {
		optabs = append(optabs, o)
	}
	sort.Slice(optabs, func(i, j int) bool {
		return optabs[i].Opcode < optabs[j].Opcode
	})

	var buf bytes.Buffer
	err := tablesTemplate.Execute(&buf, struct {
		Ylists []*ytabList
		Optabs []*optab
	}{
		Ylists: ylists,
		Optabs: optabs,
	})
	if err != nil {
		log.Fatalf("template execute error: %v", err)
	}

	// TODO: invoke "go fmt" or format.Gofmt? #22695.
	prettyCode, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("gofmt error: %v", err)
	}

	if _, err := w.Write(prettyCode); err != nil {
		log.Fatalf("write output: %v", err)
	}
}
