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

#include <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"

// Matches real RtoB but can be used in global initializer.
#define RtoB(r) (1<<((r)-D_AX))

enum {
	AX = RtoB(D_AX),
	BX = RtoB(D_BX),
	CX = RtoB(D_CX),
	DX = RtoB(D_DX),
	DI = RtoB(D_DI),
	SI = RtoB(D_SI),
	
	LeftRdwr = LeftRead | LeftWrite,
	RightRdwr = RightRead | RightWrite,
};

#undef RtoB

// This table gives the basic information about instruction
// generated by the compiler and processed in the optimizer.
// See opt.h for bit definitions.
//
// Instructions not generated need not be listed.
// As an exception to that rule, we typically write down all the
// size variants of an operation even if we just use a subset.
//
// The table is formatted for 8-space tabs.
static ProgInfo progtable[ALAST] = {
	[ATYPE]=	{Pseudo | Skip},
	[ATEXT]=	{Pseudo},
	[AFUNCDATA]=	{Pseudo},
	[APCDATA]=	{Pseudo},
	[AUNDEF]=	{OK},
	[AUSEFIELD]=	{OK},

	// NOP is an internal no-op that also stands
	// for USED and SET annotations, not the Intel opcode.
	[ANOP]=		{LeftRead | RightWrite},

	[AADCL]=	{SizeL | LeftRead | RightRdwr | SetCarry | UseCarry},
	[AADCQ]=	{SizeQ | LeftRead | RightRdwr | SetCarry | UseCarry},
	[AADCW]=	{SizeW | LeftRead | RightRdwr | SetCarry | UseCarry},

	[AADDB]=	{SizeB | LeftRead | RightRdwr | SetCarry},
	[AADDL]=	{SizeL | LeftRead | RightRdwr | SetCarry},
	[AADDW]=	{SizeW | LeftRead | RightRdwr | SetCarry},
	[AADDQ]=	{SizeQ | LeftRead | RightRdwr | SetCarry},
	
	[AADDSD]=	{SizeD | LeftRead | RightRdwr},
	[AADDSS]=	{SizeF | LeftRead | RightRdwr},

	[AANDB]=	{SizeB | LeftRead | RightRdwr | SetCarry},
	[AANDL]=	{SizeL | LeftRead | RightRdwr | SetCarry},
	[AANDQ]=	{SizeQ | LeftRead | RightRdwr | SetCarry},
	[AANDW]=	{SizeW | LeftRead | RightRdwr | SetCarry},

	[ACALL]=	{RightAddr | Call | KillCarry},

	[ACDQ]=		{OK, AX, AX | DX},
	[ACQO]=		{OK, AX, AX | DX},
	[ACWD]=		{OK, AX, AX | DX},

	[ACLD]=		{OK},
	[ASTD]=		{OK},

	[ACMPB]=	{SizeB | LeftRead | RightRead | SetCarry},
	[ACMPL]=	{SizeL | LeftRead | RightRead | SetCarry},
	[ACMPQ]=	{SizeQ | LeftRead | RightRead | SetCarry},
	[ACMPW]=	{SizeW | LeftRead | RightRead | SetCarry},

	[ACOMISD]=	{SizeD | LeftRead | RightRead | SetCarry},
	[ACOMISS]=	{SizeF | LeftRead | RightRead | SetCarry},

	[ACVTSD2SL]=	{SizeL | LeftRead | RightWrite | Conv},
	[ACVTSD2SQ]=	{SizeQ | LeftRead | RightWrite | Conv},
	[ACVTSD2SS]=	{SizeF | LeftRead | RightWrite | Conv},
	[ACVTSL2SD]=	{SizeD | LeftRead | RightWrite | Conv},
	[ACVTSL2SS]=	{SizeF | LeftRead | RightWrite | Conv},
	[ACVTSQ2SD]=	{SizeD | LeftRead | RightWrite | Conv},
	[ACVTSQ2SS]=	{SizeF | LeftRead | RightWrite | Conv},
	[ACVTSS2SD]=	{SizeD | LeftRead | RightWrite | Conv},
	[ACVTSS2SL]=	{SizeL | LeftRead | RightWrite | Conv},
	[ACVTSS2SQ]=	{SizeQ | LeftRead | RightWrite | Conv},
	[ACVTTSD2SL]=	{SizeL | LeftRead | RightWrite | Conv},
	[ACVTTSD2SQ]=	{SizeQ | LeftRead | RightWrite | Conv},
	[ACVTTSS2SL]=	{SizeL | LeftRead | RightWrite | Conv},
	[ACVTTSS2SQ]=	{SizeQ | LeftRead | RightWrite | Conv},

	[ADECB]=	{SizeB | RightRdwr},
	[ADECL]=	{SizeL | RightRdwr},
	[ADECQ]=	{SizeQ | RightRdwr},
	[ADECW]=	{SizeW | RightRdwr},

	[ADIVB]=	{SizeB | LeftRead, AX, AX},
	[ADIVL]=	{SizeL | LeftRead, AX|DX, AX|DX},
	[ADIVQ]=	{SizeQ | LeftRead, AX|DX, AX|DX},
	[ADIVW]=	{SizeW | LeftRead, AX|DX, AX|DX},

	[ADIVSD]=	{SizeD | LeftRead | RightRdwr},
	[ADIVSS]=	{SizeF | LeftRead | RightRdwr},

	[AIDIVB]=	{SizeB | LeftRead, AX, AX},
	[AIDIVL]=	{SizeL | LeftRead, AX|DX, AX|DX},
	[AIDIVQ]=	{SizeQ | LeftRead, AX|DX, AX|DX},
	[AIDIVW]=	{SizeW | LeftRead, AX|DX, AX|DX},

	[AIMULB]=	{SizeB | LeftRead | SetCarry, AX, AX},
	[AIMULL]=	{SizeL | LeftRead | ImulAXDX | SetCarry},
	[AIMULQ]=	{SizeQ | LeftRead | ImulAXDX | SetCarry},
	[AIMULW]=	{SizeW | LeftRead | ImulAXDX | SetCarry},

	[AINCB]=	{SizeB | RightRdwr},
	[AINCL]=	{SizeL | RightRdwr},
	[AINCQ]=	{SizeQ | RightRdwr},
	[AINCW]=	{SizeW | RightRdwr},

	[AJCC]=		{Cjmp | UseCarry},
	[AJCS]=		{Cjmp | UseCarry},
	[AJEQ]=		{Cjmp | UseCarry},
	[AJGE]=		{Cjmp | UseCarry},
	[AJGT]=		{Cjmp | UseCarry},
	[AJHI]=		{Cjmp | UseCarry},
	[AJLE]=		{Cjmp | UseCarry},
	[AJLS]=		{Cjmp | UseCarry},
	[AJLT]=		{Cjmp | UseCarry},
	[AJMI]=		{Cjmp | UseCarry},
	[AJNE]=		{Cjmp | UseCarry},
	[AJOC]=		{Cjmp | UseCarry},
	[AJOS]=		{Cjmp | UseCarry},
	[AJPC]=		{Cjmp | UseCarry},
	[AJPL]=		{Cjmp | UseCarry},
	[AJPS]=		{Cjmp | UseCarry},

	[AJMP]=		{Jump | Break | KillCarry},

	[ALEAQ]=	{LeftAddr | RightWrite},

	[AMOVBLSX]=	{SizeL | LeftRead | RightWrite | Conv},
	[AMOVBLZX]=	{SizeL | LeftRead | RightWrite | Conv},
	[AMOVBQSX]=	{SizeQ | LeftRead | RightWrite | Conv},
	[AMOVBQZX]=	{SizeQ | LeftRead | RightWrite | Conv},
	[AMOVBWSX]=	{SizeW | LeftRead | RightWrite | Conv},
	[AMOVBWZX]=	{SizeW | LeftRead | RightWrite | Conv},
	[AMOVLQSX]=	{SizeQ | LeftRead | RightWrite | Conv},
	[AMOVLQZX]=	{SizeQ | LeftRead | RightWrite | Conv},
	[AMOVWLSX]=	{SizeL | LeftRead | RightWrite | Conv},
	[AMOVWLZX]=	{SizeL | LeftRead | RightWrite | Conv},
	[AMOVWQSX]=	{SizeQ | LeftRead | RightWrite | Conv},
	[AMOVWQZX]=	{SizeQ | LeftRead | RightWrite | Conv},
	[AMOVQL]=	{SizeL | LeftRead | RightWrite | Conv},

	[AMOVB]=	{SizeB | LeftRead | RightWrite | Move},
	[AMOVL]=	{SizeL | LeftRead | RightWrite | Move},
	[AMOVQ]=	{SizeQ | LeftRead | RightWrite | Move},
	[AMOVW]=	{SizeW | LeftRead | RightWrite | Move},

	[AMOVSB]=	{OK, DI|SI, DI|SI},
	[AMOVSL]=	{OK, DI|SI, DI|SI},
	[AMOVSQ]=	{OK, DI|SI, DI|SI},
	[AMOVSW]=	{OK, DI|SI, DI|SI},

	[AMOVSD]=	{SizeD | LeftRead | RightWrite | Move},
	[AMOVSS]=	{SizeF | LeftRead | RightWrite | Move},

	// We use MOVAPD as a faster synonym for MOVSD.
	[AMOVAPD]=	{SizeD | LeftRead | RightWrite | Move},

	[AMULB]=	{SizeB | LeftRead | SetCarry, AX, AX},
	[AMULL]=	{SizeL | LeftRead | SetCarry, AX, AX|DX},
	[AMULQ]=	{SizeQ | LeftRead | SetCarry, AX, AX|DX},
	[AMULW]=	{SizeW | LeftRead | SetCarry, AX, AX|DX},
	
	[AMULSD]=	{SizeD | LeftRead | RightRdwr},
	[AMULSS]=	{SizeF | LeftRead | RightRdwr},

	[ANEGB]=	{SizeB | RightRdwr | SetCarry},
	[ANEGL]=	{SizeL | RightRdwr | SetCarry},
	[ANEGQ]=	{SizeQ | RightRdwr | SetCarry},
	[ANEGW]=	{SizeW | RightRdwr | SetCarry},

	[ANOTB]=	{SizeB | RightRdwr},
	[ANOTL]=	{SizeL | RightRdwr},
	[ANOTQ]=	{SizeQ | RightRdwr},
	[ANOTW]=	{SizeW | RightRdwr},

	[AORB]=		{SizeB | LeftRead | RightRdwr | SetCarry},
	[AORL]=		{SizeL | LeftRead | RightRdwr | SetCarry},
	[AORQ]=		{SizeQ | LeftRead | RightRdwr | SetCarry},
	[AORW]=		{SizeW | LeftRead | RightRdwr | SetCarry},

	[APOPQ]=	{SizeQ | RightWrite},
	[APUSHQ]=	{SizeQ | LeftRead},

	[ARCLB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
	[ARCLL]=	{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
	[ARCLQ]=	{SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
	[ARCLW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},

	[ARCRB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
	[ARCRL]=	{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
	[ARCRQ]=	{SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
	[ARCRW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},

	[AREP]=		{OK, CX, CX},
	[AREPN]=	{OK, CX, CX},

	[ARET]=		{Break | KillCarry},

	[AROLB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[AROLL]=	{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[AROLQ]=	{SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[AROLW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},

	[ARORB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ARORL]=	{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ARORQ]=	{SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ARORW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},

	[ASALB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASALL]=	{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASALQ]=	{SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASALW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},

	[ASARB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASARL]=	{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASARQ]=	{SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASARW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},

	[ASBBB]=	{SizeB | LeftRead | RightRdwr | SetCarry | UseCarry},
	[ASBBL]=	{SizeL | LeftRead | RightRdwr | SetCarry | UseCarry},
	[ASBBQ]=	{SizeQ | LeftRead | RightRdwr | SetCarry | UseCarry},
	[ASBBW]=	{SizeW | LeftRead | RightRdwr | SetCarry | UseCarry},

	[ASHLB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASHLL]=	{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASHLQ]=	{SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASHLW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},

	[ASHRB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASHRL]=	{SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASHRQ]=	{SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
	[ASHRW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},

	[ASTOSB]=	{OK, AX|DI, DI},
	[ASTOSL]=	{OK, AX|DI, DI},
	[ASTOSQ]=	{OK, AX|DI, DI},
	[ASTOSW]=	{OK, AX|DI, DI},

	[ASUBB]=	{SizeB | LeftRead | RightRdwr | SetCarry},
	[ASUBL]=	{SizeL | LeftRead | RightRdwr | SetCarry},
	[ASUBQ]=	{SizeQ | LeftRead | RightRdwr | SetCarry},
	[ASUBW]=	{SizeW | LeftRead | RightRdwr | SetCarry},

	[ASUBSD]=	{SizeD | LeftRead | RightRdwr},
	[ASUBSS]=	{SizeF | LeftRead | RightRdwr},

	[ATESTB]=	{SizeB | LeftRead | RightRead},
	[ATESTL]=	{SizeL | LeftRead | RightRead},
	[ATESTQ]=	{SizeQ | LeftRead | RightRead},
	[ATESTW]=	{SizeW | LeftRead | RightRead},

	[AUCOMISD]=	{SizeD | LeftRead | RightRead},
	[AUCOMISS]=	{SizeF | LeftRead | RightRead},

	[AXCHGB]=	{SizeB | LeftRdwr | RightRdwr},
	[AXCHGL]=	{SizeL | LeftRdwr | RightRdwr},
	[AXCHGQ]=	{SizeQ | LeftRdwr | RightRdwr},
	[AXCHGW]=	{SizeW | LeftRdwr | RightRdwr},

	[AXORB]=	{SizeB | LeftRead | RightRdwr | SetCarry},
	[AXORL]=	{SizeL | LeftRead | RightRdwr | SetCarry},
	[AXORQ]=	{SizeQ | LeftRead | RightRdwr | SetCarry},
	[AXORW]=	{SizeW | LeftRead | RightRdwr | SetCarry},
};

void
proginfo(ProgInfo *info, Prog *p)
{
	*info = progtable[p->as];
	if(info->flags == 0)
		fatal("unknown instruction %P", p);

	if((info->flags & ShiftCX) && p->from.type != D_CONST)
		info->reguse |= CX;

	if(info->flags & ImulAXDX) {
		if(p->to.type == D_NONE) {
			info->reguse |= AX;
			info->regset |= AX | DX;
		} else {
			info->flags |= RightRdwr;
		}
	}

	// Addressing makes some registers used.
	if(p->from.type >= D_INDIR)
		info->regindex |= RtoB(p->from.type-D_INDIR);
	if(p->from.index != D_NONE)
		info->regindex |= RtoB(p->from.index);
	if(p->to.type >= D_INDIR)
		info->regindex |= RtoB(p->to.type-D_INDIR);
	if(p->to.index != D_NONE)
		info->regindex |= RtoB(p->to.index);
}