// Copyright 2017 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 arm64asm

import (
	"strings"
	"testing"
)

func TestObjdumpARM64TestDecodeGNUSyntaxdata(t *testing.T) {
	testObjdumpARM64(t, testdataCases(t, "gnu"))
}
func TestObjdumpARM64TestDecodeGoSyntaxdata(t *testing.T) {
	testObjdumpARM64(t, testdataCases(t, "plan9"))
}
func TestObjdumpARM64Manual(t *testing.T) { testObjdumpARM64(t, hexCases(t, objdumpManualTests)) }
func TestObjdumpARM64Cond(t *testing.T)   { testObjdumpARM64(t, condCases(t)) }
func TestObjdumpARM64(t *testing.T)       { testObjdumpARM64(t, JSONCases(t)) }

// objdumpManualTests holds test cases that will be run by TestObjdumpARMManual.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=Manual, particularly with tracing enabled.
// Note that these are byte sequences, so they must be reversed from the usual
// word presentation.
var objdumpManualTests = `
bf2003d5
9f2003d5
7f2003d5
5f2003d5
3f2003d5
1f2003d5
df4d03d5
ff4d03d5
28d91b14
da6cb530
15e5e514
ff4603d5
df4803d5
bf4100d5
9f3f03d5
9f3e03d5
9f3d03d5
9f3b03d5
9f3a03d5
9f3903d5
9f3703d5
9f3603d5
9f3503d5
9f3303d5
9f3203d5
9f3103d5
ff4603d5
df4803d5
bf4100d5
a3681b53
47dc78d3
0500a012
0500e092
0500a052
0500a0d2
cd5a206e
cd5a202e
743d050e
743d0a0e
743d0c0e
743d084e
`

// allowedMismatchObjdump reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchObjdump(text string, inst *Inst, dec ExtInst) bool {
	// Skip unsupported instructions
	if hasPrefix(dec.text, todo...) {
		return true
	}
	// GNU objdump has incorrect alias conditions for following instructions
	if inst.Enc&0x000003ff == 0x000003ff && hasPrefix(dec.text, "negs") && hasPrefix(text, "cmp") {
		return true
	}
	// GNU objdump "NV" is equal to our "AL"
	if strings.HasSuffix(dec.text, " nv") && strings.HasSuffix(text, " al") {
		return true
	}
	if strings.HasPrefix(dec.text, "b.nv") && strings.HasPrefix(text, "b.al") {
		return true
	}
	// GNU objdump recognizes invalid binaries as following instructions
	if hasPrefix(dec.text, "hint", "mrs", "msr", "bfc", "orr", "mov") {
		return true
	}
	if strings.HasPrefix(text, "hint") {
		return true
	}
	// GNU objdump recognizes reserved valuse as valid ones
	if strings.Contains(text, "unknown instruction") && hasPrefix(dec.text, "fmla", "fmul", "fmulx", "fcvtzs", "fcvtzu", "fmls", "fmov", "scvtf", "ucvtf") {
		return true
	}
	// Some old objdump recognizes ldur*/stur*/prfum as ldr*/str*/prfm
	for k, v := range oldObjdumpMismatch {
		if strings.HasPrefix(dec.text, k) && strings.Replace(dec.text, k, v, 1) == text {
			return true
		}
	}
	// New objdump supports some newer mnemonics than this package. This
	// package should be updated to support the new mnemonics and the sense
	// of this reversed to continue passing with older objdumps but that
	// requires internal ARM tooling.
	if newForm, ok := newMnemonics[text]; ok && newForm == dec.text {
		return true
	}
	// GNU objdump misses spaces between operands for some instructions (e.g., "ld1 {v10.2s, v11.2s}, [x23],#16")
	if strings.Replace(text, " ", "", -1) == strings.Replace(dec.text, " ", "", -1) {
		return true
	}
	return false
}

// TODO: system instruction.
var todo = strings.Fields(`
	sys
	at
	ic
	hvc
	smc
`)

// Following instructions can't be covered because they are just aliases to another instructions which are always preferred
var Ncover = strings.Fields(`
	sbfm
	asrv
	bfm
	ubfm
	lslv
	lsrv
	rorv
	ins
	dup
`)

// Some old objdump wrongly decodes following instructions and allow their mismatches to avoid false alarm
var oldObjdumpMismatch = map[string]string{
	//oldObjValue	correctValue
	"ldr":   "ldur",
	"ldrb":  "ldurb",
	"ldrh":  "ldurh",
	"ldrsb": "ldursb",
	"ldrsh": "ldursh",
	"ldrsw": "ldursw",
	"str":   "stur",
	"strb":  "sturb",
	"strh":  "sturh",
	"prfm":  "prfum",
}

var newMnemonics = map[string]string{
	"dsb #0x00": "ssbb",
	"dsb #0x04": "pssbb",
}
