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

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

enum {
	LeftRdwr = LeftRead | LeftWrite,
	RightRdwr = RightRead | RightWrite,
};

// 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 Power opcode.
	[ANOP]=		{LeftRead | RightWrite},

	// Integer
	[AADD]=		{SizeQ | LeftRead | RegRead | RightWrite},
	[ASUB]=		{SizeQ | LeftRead | RegRead | RightWrite},
	[ANEG]=		{SizeQ | LeftRead | RegRead | RightWrite},
	[AAND]=		{SizeQ | LeftRead | RegRead | RightWrite},
	[AOR]=		{SizeQ | LeftRead | RegRead | RightWrite},
	[AXOR]=		{SizeQ | LeftRead | RegRead | RightWrite},
	[AMULLD]=	{SizeQ | LeftRead | RegRead | RightWrite},
	[AMULLW]=	{SizeL | LeftRead | RegRead | RightWrite},
	[AMULHD]=	{SizeL | LeftRead | RegRead | RightWrite},
	[AMULHDU]=	{SizeL | LeftRead | RegRead | RightWrite},
	[ADIVD]=	{SizeQ | LeftRead | RegRead | RightWrite},
	[ADIVDU]=	{SizeQ | LeftRead | RegRead | RightWrite},
	[ASLD]=		{SizeQ | LeftRead | RegRead | RightWrite},
	[ASRD]=		{SizeQ | LeftRead | RegRead | RightWrite},
	[ASRAD]=	{SizeQ | LeftRead | RegRead | RightWrite},
	[ACMP]=		{SizeQ | LeftRead | RightRead},
	[ACMPU]=	{SizeQ | LeftRead | RightRead},
	[ATD]=		{SizeQ | RightRead},

	// Floating point.
	[AFADD]=	{SizeD | LeftRead | RegRead | RightWrite},
	[AFADDS]=	{SizeF | LeftRead | RegRead | RightWrite},
	[AFSUB]=	{SizeD | LeftRead | RegRead | RightWrite},
	[AFSUBS]=	{SizeF | LeftRead | RegRead | RightWrite},
	[AFMUL]=	{SizeD | LeftRead | RegRead | RightWrite},
	[AFMULS]=	{SizeF | LeftRead | RegRead | RightWrite},
	[AFDIV]=	{SizeD | LeftRead | RegRead | RightWrite},
	[AFDIVS]=	{SizeF | LeftRead | RegRead | RightWrite},
	[AFCTIDZ]=	{SizeF | LeftRead | RegRead | RightWrite},
	[AFCFID]=	{SizeF | LeftRead | RegRead | RightWrite},
	[AFCMPU]=	{SizeD | LeftRead | RightRead},
	[AFRSP]=	{SizeD | LeftRead | RightWrite | Conv},

	// Moves
	[AMOVB]=	{SizeB | LeftRead | RightWrite | Move | Conv},
	[AMOVBU]=	{SizeB | LeftRead | RightWrite | Move | Conv | PostInc},
	[AMOVBZ]=	{SizeB | LeftRead | RightWrite | Move | Conv},
	[AMOVH]=	{SizeW | LeftRead | RightWrite | Move | Conv},
	[AMOVHU]=	{SizeW | LeftRead | RightWrite | Move | Conv | PostInc},
	[AMOVHZ]=	{SizeW | LeftRead | RightWrite | Move | Conv},
	[AMOVW]=	{SizeL | LeftRead | RightWrite | Move | Conv},
	// there is no AMOVWU.
	[AMOVWZU]=	{SizeL | LeftRead | RightWrite | Move | Conv | PostInc},
	[AMOVWZ]=	{SizeL | LeftRead | RightWrite | Move | Conv},
	[AMOVD]=	{SizeQ | LeftRead | RightWrite | Move},
	[AMOVDU]=	{SizeQ | LeftRead | RightWrite | Move | PostInc},
	[AFMOVS]=	{SizeF | LeftRead | RightWrite | Move | Conv},
	[AFMOVD]=	{SizeD | LeftRead | RightWrite | Move},

	// Jumps
	[ABR]=		{Jump | Break},
	[ABL]=		{Call},
	[ABEQ]=		{Cjmp},
	[ABNE]=		{Cjmp},
	[ABGE]=		{Cjmp},
	[ABLT]=		{Cjmp},
	[ABGT]=		{Cjmp},
	[ABLE]=		{Cjmp},
	[ARETURN]=	{Break},
	// In addtion, duffzero reads R0,R2 and writes R2.  This fact must be
	// encoded in peep.c (TODO)
	[ADUFFZERO]=	{Call},
	// In addtion, duffcopy reads R0,R2,R3 and writes R2,R3.  This fact must be
	// encoded in peep.c (TODO)
	[ADUFFCOPY]=	{Call},
};

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

	if((info->flags & RegRead) && p->reg == NREG) {
		info->flags &= ~RegRead;
		info->flags |= /*CanRegRead |*/ RightRead;
	}

	if(p->from.type == D_OREG && p->from.reg != NREG) {
		info->reguse |= RtoB(p->from.reg);
		if(info->flags & PostInc) {
			info->regset |= RtoB(p->from.reg);
		}
	}
	if(p->to.type == D_OREG && p->to.reg != NREG) {
		info->reguse |= RtoB(p->to.reg);
		if(info->flags & PostInc) {
			info->regset |= RtoB(p->to.reg);
		}
	}

	if(p->from.type == D_CONST && p->from.sym != nil && (info->flags & LeftRead)) {
		info->flags &= ~LeftRead;
		info->flags |= LeftAddr;
	}
}
