// 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]=	{Break},
	[AUSEFIELD]=	{OK},
	[ACHECKNIL]=	{LeftRead},
	[AVARDEF]=	{Pseudo | RightWrite},
	[AVARKILL]=	{Pseudo | RightWrite},

	// 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},
	[AADCW]=	{SizeW | LeftRead | RightRdwr | SetCarry | UseCarry},

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

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

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

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

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

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

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

	[ACVTSD2SL]=	{SizeL | LeftRead | RightWrite | Conv},
	[ACVTSD2SS]=	{SizeF | LeftRead | RightWrite | Conv},
	[ACVTSL2SD]=	{SizeD | LeftRead | RightWrite | Conv},
	[ACVTSL2SS]=	{SizeF | LeftRead | RightWrite | Conv},
	[ACVTSS2SD]=	{SizeD | LeftRead | RightWrite | Conv},
	[ACVTSS2SL]=	{SizeL | LeftRead | RightWrite | Conv},
	[ACVTTSD2SL]=	{SizeL | LeftRead | RightWrite | Conv},
	[ACVTTSS2SL]=	{SizeL | LeftRead | RightWrite | Conv},

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

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

	[ADIVSD]=	{SizeD | LeftRead | RightRdwr},
	[ADIVSS]=	{SizeF | LeftRead | RightRdwr},
	
	[AFLDCW]=	{SizeW | LeftAddr},
	[AFSTCW]=	{SizeW | RightAddr},

	[AFSTSW]=	{SizeW | RightAddr | RightWrite},

	[AFADDD]=	{SizeD | LeftAddr | RightRdwr},
	[AFADDDP]=	{SizeD | LeftAddr | RightRdwr},
	[AFADDF]=	{SizeF | LeftAddr | RightRdwr},

	[AFCOMD]=	{SizeD | LeftAddr | RightRead},
	[AFCOMDP]=	{SizeD | LeftAddr | RightRead},
	[AFCOMDPP]=	{SizeD | LeftAddr | RightRead},
	[AFCOMF]=	{SizeF | LeftAddr | RightRead},
	[AFCOMFP]=	{SizeF | LeftAddr | RightRead},
	[AFUCOMIP]=	{SizeF | LeftAddr | RightRead},

	[AFCHS]=	{SizeD | RightRdwr}, // also SizeF

	[AFDIVDP]=	{SizeD | LeftAddr | RightRdwr},
	[AFDIVF]=	{SizeF | LeftAddr | RightRdwr},
	[AFDIVD]=	{SizeD | LeftAddr | RightRdwr},

	[AFDIVRDP]=	{SizeD | LeftAddr | RightRdwr},
	[AFDIVRF]=	{SizeF | LeftAddr | RightRdwr},
	[AFDIVRD]=	{SizeD | LeftAddr | RightRdwr},

	[AFXCHD]=	{SizeD | LeftRdwr | RightRdwr},

	[AFSUBD]=	{SizeD | LeftAddr | RightRdwr},
	[AFSUBDP]=	{SizeD | LeftAddr | RightRdwr},
	[AFSUBF]=	{SizeF | LeftAddr | RightRdwr},
	[AFSUBRD]=	{SizeD | LeftAddr | RightRdwr},
	[AFSUBRDP]=	{SizeD | LeftAddr | RightRdwr},
	[AFSUBRF]=	{SizeF | LeftAddr | RightRdwr},

	[AFMOVD]=	{SizeD | LeftAddr | RightWrite},
	[AFMOVF]=	{SizeF | LeftAddr | RightWrite},
	[AFMOVL]=	{SizeL | LeftAddr | RightWrite},
	[AFMOVW]=	{SizeW | LeftAddr | RightWrite},
	[AFMOVV]=	{SizeQ | LeftAddr | RightWrite},

	// These instructions are marked as RightAddr
	// so that the register optimizer does not try to replace the
	// memory references with integer register references.
	// But they do not use the previous value at the address, so
	// we also mark them RightWrite.
	[AFMOVDP]=	{SizeD | LeftRead | RightWrite | RightAddr},
	[AFMOVFP]=	{SizeF | LeftRead | RightWrite | RightAddr},
	[AFMOVLP]=	{SizeL | LeftRead | RightWrite | RightAddr},
	[AFMOVWP]=	{SizeW | LeftRead | RightWrite | RightAddr},
	[AFMOVVP]=	{SizeQ | LeftRead | RightWrite | RightAddr},

	[AFMULD]=	{SizeD | LeftAddr | RightRdwr},
	[AFMULDP]=	{SizeD | LeftAddr | RightRdwr},
	[AFMULF]=	{SizeF | LeftAddr | RightRdwr},

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

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

	[AINCB]=	{SizeB | RightRdwr},
	[AINCL]=	{SizeL | 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},

	[ALEAL]=	{LeftAddr | RightWrite},

	[AMOVBLSX]=	{SizeL | LeftRead | RightWrite | Conv},
	[AMOVBLZX]=	{SizeL | LeftRead | RightWrite | Conv},
	[AMOVBWSX]=	{SizeW | LeftRead | RightWrite | Conv},
	[AMOVBWZX]=	{SizeW | LeftRead | RightWrite | Conv},
	[AMOVWLSX]=	{SizeL | LeftRead | RightWrite | Conv},
	[AMOVWLZX]=	{SizeL | LeftRead | RightWrite | Conv},

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

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

	[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},
	[AMULW]=	{SizeW | LeftRead | SetCarry, AX, AX|DX},
	
	[AMULSD]=	{SizeD | LeftRead | RightRdwr},
	[AMULSS]=	{SizeF | LeftRead | RightRdwr},

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

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

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

	[APOPL]=	{SizeL | RightWrite},
	[APUSHL]=	{SizeL | LeftRead},

	[ARCLB]=	{SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
	[ARCLL]=	{SizeL | 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},
	[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},
	[AROLW]=	{SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},

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

	[ASAHF]=	{OK, AX, AX},

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

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

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

	[ASETCC]=	{SizeB | RightRdwr | UseCarry},
	[ASETCS]=	{SizeB | RightRdwr | UseCarry},
	[ASETEQ]=	{SizeB | RightRdwr | UseCarry},
	[ASETGE]=	{SizeB | RightRdwr | UseCarry},
	[ASETGT]=	{SizeB | RightRdwr | UseCarry},
	[ASETHI]=	{SizeB | RightRdwr | UseCarry},
	[ASETLE]=	{SizeB | RightRdwr | UseCarry},
	[ASETLS]=	{SizeB | RightRdwr | UseCarry},
	[ASETLT]=	{SizeB | RightRdwr | UseCarry},
	[ASETMI]=	{SizeB | RightRdwr | UseCarry},
	[ASETNE]=	{SizeB | RightRdwr | UseCarry},
	[ASETOC]=	{SizeB | RightRdwr | UseCarry},
	[ASETOS]=	{SizeB | RightRdwr | UseCarry},
	[ASETPC]=	{SizeB | RightRdwr | UseCarry},
	[ASETPL]=	{SizeB | RightRdwr | UseCarry},
	[ASETPS]=	{SizeB | RightRdwr | UseCarry},

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

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

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

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

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

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

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

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

	[AXORB]=	{SizeB | LeftRead | RightRdwr | SetCarry},
	[AXORL]=	{SizeL | 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);
}
